Compare commits
623 Commits
VRFS-5280_
...
develop
| Author | SHA1 | Date |
|---|---|---|
|
|
dfe5facad6 | |
|
|
abae34c108 | |
|
|
df8cf11e37 | |
|
|
7baf003b90 | |
|
|
1944602b97 | |
|
|
86e151d239 | |
|
|
6a1506bbe8 | |
|
|
64a93dd42f | |
|
|
eb52813822 | |
|
|
8613a03d00 | |
|
|
8f0b8929ba | |
|
|
08e1c5274d | |
|
|
5f347ccfac | |
|
|
ca2bf19b7a | |
|
|
fad1f9d6d9 | |
|
|
51838fb413 | |
|
|
45cb401112 | |
|
|
9078515984 | |
|
|
52ae83e2f4 | |
|
|
b454cf9ead | |
|
|
65f5624ff9 | |
|
|
e82450dfe9 | |
|
|
f1992eaa78 | |
|
|
bdeecc76c6 | |
|
|
8c8024c12b | |
|
|
c183af3d6b | |
|
|
ab424c21d2 | |
|
|
753f35b24d | |
|
|
624853c868 | |
|
|
71d8571eb9 | |
|
|
fae16c7483 | |
|
|
253a6c566b | |
|
|
ed561be4b5 | |
|
|
eb69640667 | |
|
|
9b0a9d1f32 | |
|
|
e1cc5483a2 | |
|
|
73d2a7a020 | |
|
|
eb298d6859 | |
|
|
93c4154648 | |
|
|
fbd871d204 | |
|
|
0d82f6ee16 | |
|
|
1279b16ec0 | |
|
|
e3cff0a825 | |
|
|
6a6e4cde09 | |
|
|
1dd15fb0aa | |
|
|
9282369e54 | |
|
|
828191d683 | |
|
|
c0031cfe3d | |
|
|
9b17546082 | |
|
|
eed3d51f4b | |
|
|
4eac68b645 | |
|
|
fe6157e8cf | |
|
|
675bf2b69c | |
|
|
4ffc0d9b3b | |
|
|
7b665325f7 | |
|
|
eab17b3340 | |
|
|
4fafe30141 | |
|
|
7f85c91601 | |
|
|
d424026f17 | |
|
|
a4f8935b3a | |
|
|
4f837ae67f | |
|
|
0053775c7e | |
|
|
9d6c71829f | |
|
|
d00a0c08f7 | |
|
|
7e2c917ca0 | |
|
|
a84a55f178 | |
|
|
25ecab2c65 | |
|
|
b2f344fd30 | |
|
|
3bba9ec619 | |
|
|
86e03e0ba7 | |
|
|
67fd15c75c | |
|
|
b7a41c6465 | |
|
|
a8d5b8e735 | |
|
|
f977b7298e | |
|
|
9a41e8a236 | |
|
|
3d113e3877 | |
|
|
c7e80a0694 | |
|
|
9058c8af1d | |
|
|
08008a6dc3 | |
|
|
8645328cce | |
|
|
38388d2afb | |
|
|
e4d5fccdf5 | |
|
|
5f3a327d35 | |
|
|
51ed748013 | |
|
|
dcdf9e55a3 | |
|
|
7c9e449c4b | |
|
|
e7923dca9b | |
|
|
55372bf83d | |
|
|
0f556bfad4 | |
|
|
817719d539 | |
|
|
cd229a0b42 | |
|
|
b6e2fe7494 | |
|
|
ad1d8ea373 | |
|
|
2d5d93787f | |
|
|
676cbaa656 | |
|
|
d4e0e41186 | |
|
|
49d3e2a4ac | |
|
|
97d23a6bcb | |
|
|
0f7b9b2884 | |
|
|
0d25814e6e | |
|
|
1e0faa7309 | |
|
|
10e6fedb5c | |
|
|
d6dc79b478 | |
|
|
1071dec044 | |
|
|
403a830157 | |
|
|
c3bd62c0cc | |
|
|
71716b2240 | |
|
|
3a6d86e23c | |
|
|
d1b9c8b19a | |
|
|
615158baf2 | |
|
|
a392d59df4 | |
|
|
8bfb339fd5 | |
|
|
15f9beecf6 | |
|
|
fcc49c52a1 | |
|
|
e9d2ed7617 | |
|
|
685a566c85 | |
|
|
4c95ad58ac | |
|
|
8519b56c5a | |
|
|
62d66fac75 | |
|
|
f4b1ab59e0 | |
|
|
0b60c8d0cc | |
|
|
d4fcb0c19f | |
|
|
98b70724a4 | |
|
|
8f9f7d90fa | |
|
|
81627fc37f | |
|
|
49888b9b5b | |
|
|
b2c2129d42 | |
|
|
35a5f9f17c | |
|
|
b942265652 | |
|
|
b657511310 | |
|
|
a687356d72 | |
|
|
f4c71f9778 | |
|
|
1120d8be31 | |
|
|
5fba0f8d97 | |
|
|
ab1575365d | |
|
|
3369b39341 | |
|
|
f5b090a20b | |
|
|
cc664889f8 | |
|
|
cfe0129a6d | |
|
|
a525082f26 | |
|
|
7f5b6152f0 | |
|
|
0df9beb786 | |
|
|
85c0c5812f | |
|
|
4ede79ba51 | |
|
|
155ee69863 | |
|
|
8d49ea0368 | |
|
|
93dc5ccd35 | |
|
|
7b6a571550 | |
|
|
0db6bac749 | |
|
|
c23305634f | |
|
|
481f8099af | |
|
|
23ce65d0bd | |
|
|
ce1c4673b2 | |
|
|
a2c607b449 | |
|
|
e4f6444d35 | |
|
|
dbdc5e5296 | |
|
|
3b45014af0 | |
|
|
55c532b44f | |
|
|
c73e78d6e6 | |
|
|
488aa4c03d | |
|
|
2cc56f7ad7 | |
|
|
e6046f74b6 | |
|
|
aa9555aa1e | |
|
|
39dfbadbd8 | |
|
|
b7732c11f8 | |
|
|
7faac7b61b | |
|
|
98cae96d5d | |
|
|
0f30107481 | |
|
|
42c77a8049 | |
|
|
69f4bc837a | |
|
|
89411738cb | |
|
|
9d7fea284a | |
|
|
198adfe395 | |
|
|
383d9deeb3 | |
|
|
7945c52cbb | |
|
|
9e84bcc0df | |
|
|
6007abd6a8 | |
|
|
be5a127b4c | |
|
|
93a824501b | |
|
|
3309c7f69a | |
|
|
b04bb0c55b | |
|
|
8cafb23b3e | |
|
|
2c76628926 | |
|
|
b678bfd70d | |
|
|
9d63882e69 | |
|
|
d4546c6975 | |
|
|
a9b9e592bb | |
|
|
dcb842035f | |
|
|
9c0e643b23 | |
|
|
94747d9fe0 | |
|
|
81cf0dadb0 | |
|
|
254d1ecac7 | |
|
|
754be7877e | |
|
|
8a8f1d14f9 | |
|
|
bd2d9410b5 | |
|
|
1e623e77de | |
|
|
a0b255a5d5 | |
|
|
342960e57b | |
|
|
36a184638f | |
|
|
1ce4d4f4e6 | |
|
|
810ae15769 | |
|
|
f26733fa46 | |
|
|
55f6839521 | |
|
|
4a3630f786 | |
|
|
71458e3983 | |
|
|
5ff152233b | |
|
|
3677181e09 | |
|
|
46111f041d | |
|
|
1dd160e19f | |
|
|
f07a96165d | |
|
|
3e9d2e371e | |
|
|
3d14ddbe74 | |
|
|
28487f271e | |
|
|
51f1a7e58e | |
|
|
a027ed7096 | |
|
|
70dd62606b | |
|
|
21301017ca | |
|
|
1772f5a46f | |
|
|
4804877452 | |
|
|
1d0a01c7ee | |
|
|
04a45394a0 | |
|
|
2c6f1ef0de | |
|
|
2786c74175 | |
|
|
8835455795 | |
|
|
2d7a6db541 | |
|
|
9b427b7a24 | |
|
|
315f532719 | |
|
|
04a7445f68 | |
|
|
6368897b21 | |
|
|
d7eef09946 | |
|
|
f8462dbb84 | |
|
|
8ddb4ce80d | |
|
|
6b66764082 | |
|
|
5673d9b21e | |
|
|
c163d6b0ba | |
|
|
61eddbb2bc | |
|
|
48547d2cb1 | |
|
|
c3563a9197 | |
|
|
08fafbf2de | |
|
|
cbaf1ea4f9 | |
|
|
d9004980e6 | |
|
|
5d87d1a358 | |
|
|
3245024925 | |
|
|
d3a9a7c1e5 | |
|
|
f389250aa8 | |
|
|
d5dcc71e35 | |
|
|
67241e7be7 | |
|
|
5ab863bc33 | |
|
|
89c4df9650 | |
|
|
e6ac929303 | |
|
|
fb3cf004b9 | |
|
|
531bf25376 | |
|
|
27b66783c1 | |
|
|
2402cf6992 | |
|
|
387a9b141d | |
|
|
625a9dc0e4 | |
|
|
a09f922463 | |
|
|
9b6f6f74e9 | |
|
|
db9ad36627 | |
|
|
ea0131cb4e | |
|
|
c5439f7a18 | |
|
|
78debaa8ab | |
|
|
8bbdedd082 | |
|
|
3c4e59faa9 | |
|
|
b532308e28 | |
|
|
63289a29af | |
|
|
5d60c3db81 | |
|
|
8f58fe4017 | |
|
|
82007bb41c | |
|
|
358f42bba2 | |
|
|
ab5b6a5fb5 | |
|
|
b04b8cfee2 | |
|
|
45e6122439 | |
|
|
d0115a8cd9 | |
|
|
ec7a79c8bb | |
|
|
df9cbf3ba7 | |
|
|
33d0de6f0c | |
|
|
8b350ca3b6 | |
|
|
abade1915e | |
|
|
eba86ccdd5 | |
|
|
a388d5e1d0 | |
|
|
98dbb358bd | |
|
|
93961806eb | |
|
|
6366bc5c6e | |
|
|
ae138b79c0 | |
|
|
ed9887f9e9 | |
|
|
3dc4af65e0 | |
|
|
ab5947d94a | |
|
|
8979d732f2 | |
|
|
b9eff8dac0 | |
|
|
c42dfdbb30 | |
|
|
94ac48c8b5 | |
|
|
ac469c2741 | |
|
|
d7a4e46355 | |
|
|
051477d19f | |
|
|
47fd0cb68d | |
|
|
371f68a616 | |
|
|
69a37495b5 | |
|
|
78ae753d87 | |
|
|
4944c5983d | |
|
|
182deaa177 | |
|
|
1281caccf2 | |
|
|
604b6f6b59 | |
|
|
996fbe51d0 | |
|
|
d4b412e223 | |
|
|
2d408eb3a7 | |
|
|
c8a8ecc40d | |
|
|
b51207dd3e | |
|
|
f2f6b978f8 | |
|
|
b8b1146320 | |
|
|
541bca6b28 | |
|
|
c1a2e76186 | |
|
|
79ba0ebc70 | |
|
|
8718ac4588 | |
|
|
0dd8e90a7f | |
|
|
3b0c2d1a42 | |
|
|
ccfb48459d | |
|
|
6d1ad051fe | |
|
|
652b33ec51 | |
|
|
ca615eb9d9 | |
|
|
8e3272c7d5 | |
|
|
5831e7d709 | |
|
|
3c3ea6cbba | |
|
|
3db0a30fef | |
|
|
7ed94b5dc0 | |
|
|
b83dfb2114 | |
|
|
29d64b7d0f | |
|
|
36bd2788b2 | |
|
|
119a180a82 | |
|
|
91c85d5e59 | |
|
|
5d171ec016 | |
|
|
4732138cbd | |
|
|
d07cff3721 | |
|
|
2afd8500bd | |
|
|
0377f73b9f | |
|
|
47b1e843ed | |
|
|
77359f7fc2 | |
|
|
d603dd2911 | |
|
|
eea46feb5d | |
|
|
7dbd061a86 | |
|
|
db6a94ded1 | |
|
|
15af2d7469 | |
|
|
9a0b4f4ddb | |
|
|
c779277b4d | |
|
|
a0dc7fa92d | |
|
|
0993c9a6b6 | |
|
|
b876e1e253 | |
|
|
3300b5a37a | |
|
|
3695b2f5d4 | |
|
|
d976cdf866 | |
|
|
fa35930c75 | |
|
|
cd1930a2bc | |
|
|
b5a18f6501 | |
|
|
fd1c0714d3 | |
|
|
4f6bf663dd | |
|
|
0c2102b308 | |
|
|
ba59e7b20b | |
|
|
3f1a3402d8 | |
|
|
c60959126c | |
|
|
c404cc0bb1 | |
|
|
ef79d3a8c0 | |
|
|
499cd7e16b | |
|
|
727147ffee | |
|
|
3175f77b7f | |
|
|
6833c68e40 | |
|
|
9668b59e23 | |
|
|
8aa463c054 | |
|
|
136a3c399d | |
|
|
e973a5f55c | |
|
|
5a00f45d5c | |
|
|
39be8f2955 | |
|
|
bca4f17c8f | |
|
|
f9aee04dd5 | |
|
|
3945e97319 | |
|
|
be1cca46b1 | |
|
|
898154a775 | |
|
|
94308e9c1d | |
|
|
828939ff68 | |
|
|
7d7dc7a620 | |
|
|
3b451427fc | |
|
|
f904bdfc1c | |
|
|
faecb69db9 | |
|
|
2cc14be759 | |
|
|
6e29efd307 | |
|
|
fb537806ca | |
|
|
22d4553f6f | |
|
|
a0e4c5fc33 | |
|
|
44f78edc63 | |
|
|
4a09e56d8b | |
|
|
7cf5648b78 | |
|
|
e55ad12b61 | |
|
|
c907d9c3ba | |
|
|
c44db20385 | |
|
|
7af01a6c61 | |
|
|
5b750cc3d9 | |
|
|
106ea91361 | |
|
|
041ebccaf1 | |
|
|
86d77df2c9 | |
|
|
a3c511d2b0 | |
|
|
adafcb8569 | |
|
|
48335a9d9c | |
|
|
5fd4015ee2 | |
|
|
e2b4c901dc | |
|
|
176ba1febe | |
|
|
24aab60556 | |
|
|
8868ff718f | |
|
|
bbe83a008f | |
|
|
e878c64c3c | |
|
|
8f63547f34 | |
|
|
97e0a8d36a | |
|
|
7176fecd7b | |
|
|
37ed2dddd4 | |
|
|
cb0cb654c8 | |
|
|
40fbc7b430 | |
|
|
81c1c17a2f | |
|
|
c2ffbff216 | |
|
|
4dd95af2fa | |
|
|
20eb17b044 | |
|
|
703309aa65 | |
|
|
3ae52098c6 | |
|
|
bd45275107 | |
|
|
edb6bd0b90 | |
|
|
cdd12d621d | |
|
|
cb1a8f54c5 | |
|
|
05d8db9cfa | |
|
|
af1140fa05 | |
|
|
7b0d6c153e | |
|
|
dbb5c4a520 | |
|
|
ee95d07dfc | |
|
|
884203844b | |
|
|
a420767446 | |
|
|
4ff3d2d83c | |
|
|
d80627b5d4 | |
|
|
73c566fa9e | |
|
|
e305898a74 | |
|
|
2a31e81c65 | |
|
|
6ad9aed71f | |
|
|
2459b3d447 | |
|
|
0476e5ebb6 | |
|
|
e5dc8b57ec | |
|
|
52c8b48d81 | |
|
|
4144d6d123 | |
|
|
1c0a6b2138 | |
|
|
17b0ea6111 | |
|
|
95008f49e6 | |
|
|
9164a1058d | |
|
|
23830144d8 | |
|
|
eb488e374b | |
|
|
8f84e5d12d | |
|
|
238fc8a382 | |
|
|
9dc101d3f6 | |
|
|
1a11cc331c | |
|
|
8cf7048b7f | |
|
|
4c161025e8 | |
|
|
6546cc08bc | |
|
|
fd9632a26c | |
|
|
861722e5d4 | |
|
|
30aa9eeed2 | |
|
|
1de7a1f2f2 | |
|
|
963cc238e8 | |
|
|
dd4239f1f3 | |
|
|
fe6c372d3d | |
|
|
df90496d30 | |
|
|
b85a2b236c | |
|
|
0406a64531 | |
|
|
3fed41899a | |
|
|
6e131f6b5d | |
|
|
c29d3459e6 | |
|
|
c16be14207 | |
|
|
aeb2e50692 | |
|
|
d2c525f498 | |
|
|
b563f22a32 | |
|
|
4accb2296f | |
|
|
23e0a7096c | |
|
|
3bf694d816 | |
|
|
b4200a1b87 | |
|
|
286841c3d0 | |
|
|
9b59ef6121 | |
|
|
5a8c85e765 | |
|
|
c07bf115e6 | |
|
|
06e085fd13 | |
|
|
9b72852a54 | |
|
|
0f25b00571 | |
|
|
8e792d1658 | |
|
|
84481a4fec | |
|
|
cff277d437 | |
|
|
b0672a9cb3 | |
|
|
4fed7cbb1a | |
|
|
684fff910c | |
|
|
64c42b5de3 | |
|
|
38f9d4869b | |
|
|
317fdb2171 | |
|
|
6d5edf93bf | |
|
|
335d7667b5 | |
|
|
0a871c738a | |
|
|
1d355a3a96 | |
|
|
c1a4481bf2 | |
|
|
5315d88903 | |
|
|
e5afb83f81 | |
|
|
812cedb0e1 | |
|
|
a07ff74dc0 | |
|
|
27a3b3c09b | |
|
|
21d6801185 | |
|
|
66a7f81317 | |
|
|
6fed528e84 | |
|
|
2b96b4d8ef | |
|
|
d8685a00bd | |
|
|
d0d2a41e96 | |
|
|
2edfccda87 | |
|
|
3062238465 | |
|
|
67fbf932ee | |
|
|
5598b2ef17 | |
|
|
60ef40a51b | |
|
|
fcd9dd15f4 | |
|
|
b7615c3fcd | |
|
|
10e67e4cbb | |
|
|
fef31f097f | |
|
|
c108bdd058 | |
|
|
f42383f0b4 | |
|
|
2102f8807a | |
|
|
c8a111f5f5 | |
|
|
e491461f64 | |
|
|
9b94ea6351 | |
|
|
7149392fe2 | |
|
|
20c8df7121 | |
|
|
1ca3f5f245 | |
|
|
1e988931a4 | |
|
|
f75d2c8c46 | |
|
|
fad3b73d67 | |
|
|
46202a2373 | |
|
|
bbab6cdb9f | |
|
|
6a57530a8b | |
|
|
b3922ec025 | |
|
|
dd03c215c4 | |
|
|
20c02f0f2a | |
|
|
02f3201cb2 | |
|
|
65bc188af1 | |
|
|
484482e5bd | |
|
|
036d982ab7 | |
|
|
fb7e4a287e | |
|
|
0a32c1fac9 | |
|
|
c5767efbaa | |
|
|
3981c47f9f | |
|
|
84e3609602 | |
|
|
199086d06b | |
|
|
fa082230b8 | |
|
|
b589ad8553 | |
|
|
981b83cea2 | |
|
|
e25b88d062 | |
|
|
550a458c8d | |
|
|
100e14cad0 | |
|
|
529d3fc891 | |
|
|
6ea439b435 | |
|
|
2b80e277c9 | |
|
|
96a93c7daf | |
|
|
6ff25ad1b7 | |
|
|
4405e08c0a | |
|
|
db7525ad64 | |
|
|
50ed5116f7 | |
|
|
17207fc0b8 | |
|
|
2de5f3a3c2 | |
|
|
e943a3235e | |
|
|
957c5dc3e9 | |
|
|
5c0ed0d773 | |
|
|
6fd7a6531b | |
|
|
a9c0315697 | |
|
|
0cac99e407 | |
|
|
f8eca9d538 | |
|
|
6e78f61f9c | |
|
|
78b9cd0156 | |
|
|
295d613165 | |
|
|
1080ac4a47 | |
|
|
c2f7fe7c32 | |
|
|
5bd67d431c | |
|
|
ee54464cfc | |
|
|
245b39f67d | |
|
|
3606b4f281 | |
|
|
0fcd8bc873 | |
|
|
84e984a549 | |
|
|
e30aacc91d | |
|
|
22e207c9cc | |
|
|
bffab7564d | |
|
|
3f5cceb031 | |
|
|
a4d8bc70b0 | |
|
|
87278e6adb | |
|
|
7f4947a590 | |
|
|
c0b9b66b9d | |
|
|
d19756b537 | |
|
|
d35778f5e2 | |
|
|
edab0fe9fc | |
|
|
ef823df8eb | |
|
|
15bd48ce3b | |
|
|
505f2d5592 | |
|
|
961183bd47 | |
|
|
8d99f362a2 | |
|
|
6c179d3220 | |
|
|
caef794231 | |
|
|
178d4fcb3f | |
|
|
1717fc1a15 | |
|
|
9e718cccdc | |
|
|
5bbc5999d7 | |
|
|
4eb009d76d | |
|
|
cb344665cb | |
|
|
c6c1e96e85 | |
|
|
80fb8b2fce | |
|
|
7a86300755 | |
|
|
97b1b03946 | |
|
|
a63b5cd61f | |
|
|
362d0a7ba9 | |
|
|
08a69da490 | |
|
|
480de3e89f | |
|
|
2110c4b1ce | |
|
|
e769eb0531 | |
|
|
4ef2dcdc04 | |
|
|
2438e45a1b | |
|
|
2ec02ee436 | |
|
|
4dcb1c4dd8 | |
|
|
e88cf16702 | |
|
|
56b49d4790 | |
|
|
f5def534fe | |
|
|
d4a517ab7c | |
|
|
8c5bba305c | |
|
|
e577d71d5d |
|
|
@ -0,0 +1,25 @@
|
|||
name: Build Admin
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: dagger
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
|
||||
- name: Install Dagger
|
||||
run: |
|
||||
curl -L https://dl.dagger.io/dagger/install.sh | sh
|
||||
sudo mv bin/dagger /usr/local/bin/
|
||||
|
||||
- name: Login to Gitea Registry
|
||||
run: echo "${{ gitea.token }}" | docker login git.staging.jamkazam.com -u ${{ gitea.actor }} --password-stdin
|
||||
|
||||
- name: Build and Publish with Dagger
|
||||
working-directory: ./admin
|
||||
run: |
|
||||
dagger call build-local --source=. --repo-root=../ publish --address=git.staging.jamkazam.com/seth/jam-cloud-admin:latest
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
name: Environment Orchestrator
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
orchestrate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Component Deployment Gatekeeper
|
||||
run: |
|
||||
# JAM_CLUSTER_ENV should be set to 'staging' or 'production' in the Gitea Runner
|
||||
ENV="${JAM_CLUSTER_ENV:-staging}"
|
||||
echo "🌐 Cluster Environment: $ENV"
|
||||
|
||||
# 1. Extract modes for this environment
|
||||
ADMIN_MODE=$(jq -r ".environments.$ENV.admin" .jk-deploy.json)
|
||||
WEB_MODE=$(jq -r ".environments.$ENV.web" .jk-deploy.json)
|
||||
WS_MODE=$(jq -r ".environments.$ENV.[\"websocket-gateway\"]" .jk-deploy.json)
|
||||
|
||||
# 2. Conditional Execution
|
||||
if [ "$ADMIN_MODE" == "short-circuit" ]; then
|
||||
echo "⚡ ADMIN: Short-circuit detected. Deploying immediately..."
|
||||
cd admin && dagger call ship --source=.
|
||||
else
|
||||
echo "⏸️ ADMIN: Mode is $ADMIN_MODE. Skipping short-circuit deploy."
|
||||
fi
|
||||
|
||||
if [ "$WEB_MODE" == "short-circuit" ]; then
|
||||
echo "⚡ WEB: Short-circuit detected. Deploying immediately..."
|
||||
cd web && dagger call ship --source=.
|
||||
else
|
||||
echo "⏸️ WEB: Mode is $WEB_MODE. Skipping short-circuit deploy."
|
||||
fi
|
||||
|
||||
if [ "$WS_MODE" == "short-circuit" ]; then
|
||||
echo "⚡ WS-GATEWAY: Short-circuit detected. Deploying immediately..."
|
||||
cd websocket-gateway && just ship
|
||||
else
|
||||
echo "⏸️ WS-GATEWAY: Mode is $WS_MODE. Skipping short-circuit deploy."
|
||||
fi
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
name: Test Runner
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "Runner is working!"
|
||||
|
|
@ -8,3 +8,7 @@ HTML
|
|||
coverage
|
||||
dump.rdb
|
||||
working.png
|
||||
ruby/.rails5-gems
|
||||
web/.rails5-gems
|
||||
websocket-gateway/.rails5-gems
|
||||
.pg_data/
|
||||
|
|
@ -0,0 +1 @@
|
|||
2.4.1
|
||||
|
|
@ -2,7 +2,7 @@ ActiveAdmin.register JamRuby::AffiliateQuarterlyPayment, :as => 'Affiliate Quart
|
|||
|
||||
menu :label => 'Quarterly Reports', :parent => 'Affiliates'
|
||||
|
||||
config.sort_order = 'due_amount_in_cents DESC'
|
||||
config.sort_order = 'year desc, quarter desc, due_amount_in_cents desc'
|
||||
config.batch_actions = false
|
||||
config.clear_action_items!
|
||||
config.filters = true
|
||||
|
|
@ -14,18 +14,32 @@ ActiveAdmin.register JamRuby::AffiliateQuarterlyPayment, :as => 'Affiliate Quart
|
|||
filter :quarter
|
||||
filter :closed
|
||||
filter :paid
|
||||
filter :jamtracks_sold
|
||||
filter :subscriptions_count
|
||||
filter :due_amount_in_cents
|
||||
|
||||
form :partial => 'form'
|
||||
|
||||
scope("Sorted By Due Amount", default: true) { |scope| scope.order('year desc, quarter desc, due_amount_in_cents desc') }
|
||||
scope("Sorted By Jamtracks Sold", default: false) { |scope| scope.order('year desc, quarter desc, jamtracks_sold desc') }
|
||||
scope("Sorted By Subs", default: false) { |scope| scope.order('year desc, quarter desc, subscriptions_count desc') }
|
||||
scope("Sorted By Newest First") { |scope| scope.order('year desc, quarter desc, id desc') }
|
||||
scope("Any") { |scope| scope.order('year desc, quarter desc, due_amount_in_cents desc') }
|
||||
|
||||
|
||||
index do
|
||||
|
||||
# default_actions # use this for all view/edit/delete links
|
||||
|
||||
column 'Year' do |oo| oo.year end
|
||||
column 'Quarter' do |oo| oo.quarter end
|
||||
column 'Partner Id' do |oo| oo.affiliate_partner.id end
|
||||
column 'Partner' do |oo| link_to(oo.affiliate_partner.display_name, oo.affiliate_partner.admin_url, {:title => oo.affiliate_partner.display_name}) end
|
||||
column "Due (\u00A2)" do |oo| oo.due_amount_in_cents end
|
||||
column 'JamTracks Sold' do |oo| oo.jamtracks_sold end
|
||||
column "Tot ($)" do |oo| sprintf("$%.2f", oo.due_amount_in_cents.to_f / 100.to_f) end
|
||||
column "Sub ($)" do |oo| sprintf("$%.2f", oo.subscription_due_amount_in_cents.to_f / 100.to_f) end
|
||||
column "Jam ($)" do |oo| sprintf("$%.2f", oo.jamtrack_due_amount_in_cents.to_f / 100.to_f) end
|
||||
column 'JamTracks' do |oo| oo.jamtracks_sold end
|
||||
column 'Subscriptions' do |oo| oo.subscriptions_count end
|
||||
column 'Paid' do |oo| oo.paid end
|
||||
column 'Closed' do |oo| oo.paid end
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ ActiveAdmin.register JamRuby::AffiliateTrafficTotal, :as => 'Affiliate Daily Sta
|
|||
# default_actions # use this for all view/edit/delete links
|
||||
|
||||
column 'Day' do |oo| oo.day end
|
||||
column 'Partner' do |oo| link_to(oo.affiliate_partner.display_name, oo.affiliate_partner.admin_url, {:title => oo.affiliate_partner.display_name}) end
|
||||
column 'Partner ID' do |oo| oo.affiliate_partner.id end
|
||||
column 'Partner Name' do |oo| oo.affiliate_partner.display_name end
|
||||
column 'Partner User' do |oo| link_to(oo.affiliate_partner.partner_user.name, admin_user_path(oo.affiliate_partner.partner_user.id), { :title => oo.affiliate_partner.partner_user.name }) end
|
||||
column 'Signups' do |oo| oo.signups end
|
||||
column 'Visits' do |oo| oo.visits end
|
||||
|
||||
|
|
@ -31,6 +33,16 @@ ActiveAdmin.register JamRuby::AffiliateTrafficTotal, :as => 'Affiliate Daily Sta
|
|||
|
||||
|
||||
controller do
|
||||
|
||||
def scoped_collection
|
||||
rel = end_of_association_chain
|
||||
.includes([:affiliate_partner])
|
||||
.order('day DESC')
|
||||
if (ref_id = params[AffiliatePartner::PARAM_REFERRAL]).present?
|
||||
qq = ['affiliate_partner_id = ?', ref_id]
|
||||
else
|
||||
qq = ['affiliate_partner_id IS NOT NULL']
|
||||
end
|
||||
@users ||= rel.where(qq)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,27 +8,32 @@ ActiveAdmin.register JamRuby::User, :as => 'Referrals' do
|
|||
config.filters = true
|
||||
|
||||
filter :affiliate_referral
|
||||
filter :email
|
||||
|
||||
## scope("Has Signups", default: true) { |scope| scope.where('visits != 0 or signups != 0').order('day desc') }
|
||||
|
||||
index do
|
||||
column 'User' do |oo| link_to(oo.name, oo.admin_url, {:title => oo.name}) end
|
||||
column 'Email' do |oo| oo.email end
|
||||
column 'User Email' do |oo| oo.email end
|
||||
column 'Created' do |oo| oo.created_at end
|
||||
column 'Partner' do |oo| oo.affiliate_referral.display_name end
|
||||
column 'Partner ID' do |oo| oo.affiliate_referral.id end
|
||||
column 'Partner Name' do |oo| oo.affiliate_referral.display_name end
|
||||
column 'Partner User' do |oo| link_to(oo.affiliate_referral.partner_user.name, admin_user_path(oo.affiliate_referral.partner_user.id), { :title => oo.affiliate_referral.partner_user.name }) end
|
||||
end
|
||||
|
||||
controller do
|
||||
|
||||
def scoped_collection
|
||||
rel = end_of_association_chain
|
||||
.includes([:affiliate_referral])
|
||||
.order('created_at DESC')
|
||||
if (ref_id = params[AffiliatePartner::PARAM_REFERRAL]).present?
|
||||
qq = ['affiliate_referral_id = ?', ref_id]
|
||||
else
|
||||
qq = ['affiliate_referral_id IS NOT NULL']
|
||||
end
|
||||
@users ||= rel.where(qq)
|
||||
def scoped_collection
|
||||
rel = end_of_association_chain
|
||||
.includes([:affiliate_referral])
|
||||
.order('created_at DESC')
|
||||
if (ref_id = params[AffiliatePartner::PARAM_REFERRAL]).present?
|
||||
qq = ['affiliate_referral_id = ?', ref_id]
|
||||
else
|
||||
qq = ['affiliate_referral_id IS NOT NULL']
|
||||
end
|
||||
@users ||= rel.where(qq)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,19 +5,35 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
|||
config.sort_order = 'referral_user_count DESC'
|
||||
config.batch_actions = false
|
||||
# config.clear_action_items!
|
||||
config.filters = false
|
||||
config.per_page = 50
|
||||
config.filters = true
|
||||
config.per_page = 100
|
||||
config.paginate = true
|
||||
|
||||
#form :partial => 'form'
|
||||
|
||||
scope("Active", default: true) { |scope| scope.where('partner_user_id IS NOT NULL').order('referral_user_count desc') }
|
||||
|
||||
#filter :partner_user
|
||||
filter :partner_name
|
||||
filter :id
|
||||
filter :current_quarter_in_cents
|
||||
filter :cumulative_earnings_in_cents
|
||||
filter :jamtracks_sold
|
||||
filter :subscriptions_count
|
||||
filter :referral_user_count
|
||||
|
||||
scope("Sorted By Current Quarter", default: true) { |scope| scope.where('partner_user_id IS NOT NULL').order('current_quarter_in_cents desc') }
|
||||
scope("Sorted By Jamtracks Sold", default: false) { |scope| scope.where('partner_user_id IS NOT NULL').order('jamtracks_sold desc') }
|
||||
scope("Sorted By Subs", default: false) { |scope| scope.where('partner_user_id IS NOT NULL').order('subscriptions_count desc') }
|
||||
scope("Sorted By Signups", default: false) { |scope| scope.where('partner_user_id IS NOT NULL').order('referral_user_count desc') }
|
||||
scope("Sorted By Newest First") { |scope| scope.where('partner_user_id IS NOT NULL').order('id desc') }
|
||||
scope("Any") { |scope| scope.where('partner_user_id IS NOT NULL').order('referral_user_count desc') }
|
||||
scope("Unpaid") { |partner| partner.unpaid }
|
||||
|
||||
controller do
|
||||
helper 'active_admin/subscription'
|
||||
end
|
||||
|
||||
|
||||
form do |f|
|
||||
f.inputs 'Fields' do
|
||||
f.input(:partner_name, :input_html => { :maxlength => 128 })
|
||||
|
|
@ -45,14 +61,47 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
|||
column 'Code' do |oo|
|
||||
oo.id
|
||||
end
|
||||
column 'Referral Count' do |oo|
|
||||
column 'Signups' do |oo|
|
||||
oo.referral_user_count
|
||||
end
|
||||
column 'Earnings' do |oo|
|
||||
sprintf("$%.2f", oo.cumulative_earnings_in_dollars)
|
||||
column 'JamTracks' do |oo|
|
||||
oo.jamtracks_sold
|
||||
end
|
||||
column 'Subs' do |oo|
|
||||
oo.subscriptions_count
|
||||
end
|
||||
column 'Cum Earnings' do |oo|
|
||||
div do
|
||||
sprintf("Tot $%.2f", oo.cumulative_earnings_in_dollars)
|
||||
end
|
||||
div do
|
||||
sprintf("Jam $%.2f", oo.jamtrack_cumulative_earnings_in_dollars)
|
||||
end
|
||||
div do
|
||||
sprintf("Sub $%.2f", oo.subscriptions_cumulative_earnings_in_dollars)
|
||||
end
|
||||
end
|
||||
column 'Current Quarter' do |oo|
|
||||
div do
|
||||
sprintf("Tot $%.2f", oo.current_quarter_in_dollars)
|
||||
end
|
||||
div do
|
||||
sprintf("Jam $%.2f", oo.jamtrack_current_quarter_in_dollars)
|
||||
end
|
||||
div do
|
||||
sprintf("Sub $%.2f", oo.subscriptions_current_quarter_in_dollars)
|
||||
end
|
||||
end
|
||||
column 'Amount Owed' do |oo|
|
||||
sprintf("$%.2f", oo.due_amount_in_cents.to_f / 100.to_f)
|
||||
div do
|
||||
sprintf("Tot $%.2f", oo.due_amount_in_cents.to_f / 100.to_f)
|
||||
end
|
||||
div do
|
||||
sprintf("Jam $%.2f", oo.jamtrack_due_amount_in_cents.to_f / 100.to_f)
|
||||
end
|
||||
div do
|
||||
sprintf("Sub $%.2f", oo.subscription_due_amount_in_cents.to_f / 100.to_f)
|
||||
end
|
||||
end
|
||||
column 'Pay Actions' do |oo|
|
||||
link_to('Mark Paid', mark_paid_admin_affiliate_path(oo.id), :confirm => "Mark this affiliate as PAID?") if oo.unpaid
|
||||
|
|
@ -71,6 +120,15 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
|||
row :address
|
||||
row :tax_identifier
|
||||
row :paypal_id
|
||||
row :venmo_user_id
|
||||
row :jamtracks_sold
|
||||
row :subscriptions_count
|
||||
row :cumulative_earnings_in_dollars
|
||||
row :jamtrack_cumulative_earnings_in_dollars
|
||||
row :subscriptions_cumulative_earnings_in_dollars
|
||||
row :current_quarter_in_dollars
|
||||
row :jamtrack_current_quarter_in_dollars
|
||||
row :subscriptions_current_quarter_in_dollars
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
ActiveAdmin.register JamRuby::AppFeature, as: 'App Features' do
|
||||
|
||||
menu parent: 'Misc', label: 'App Features'
|
||||
|
||||
config.sort_order = 'created_at ASC'
|
||||
config.batch_actions = false
|
||||
config.filters = false
|
||||
config.per_page = 50
|
||||
config.paginate = true
|
||||
|
||||
form do |f|
|
||||
f.inputs 'Fields' do
|
||||
f.input(:feature_type, as: :select, collection: JamRuby::AppFeature::FEATURE_TYPES)
|
||||
f.input(:handle, :input_html => { :maxlength => 1025 })
|
||||
f.input(:is_enabled, as: :boolean)
|
||||
f.input(:env, as: :select, collection: %w(production staging development))
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -640,7 +640,12 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
autocomplete :user, :email, :full => true, :display_value => :autocomplete_display_name, extra_data: [:last_jam_addr]
|
||||
|
||||
def get_autocomplete_items(parameters)
|
||||
User.select("email, first_name, last_name, id, last_jam_addr").where(["email ILIKE ? OR first_name ILIKE ? OR last_name ILIKE ?", "%#{parameters[:term]}%", "%#{parameters[:term]}%", "%#{parameters[:term]}%"])
|
||||
term = parameters[:term]
|
||||
if term.include?('@')
|
||||
User.select("email, first_name, last_name, id, last_jam_addr").where(["email = ?", term]).limit(5)
|
||||
else
|
||||
User.select("email, first_name, last_name, id, last_jam_addr").where(["email ILIKE ? OR first_name ILIKE ? OR last_name ILIKE ?", "%#{term}%", "%#{term}%", "%#{term}%"]).limit(40)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -699,6 +704,64 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
|
||||
end
|
||||
|
||||
def user_latency_recommendation
|
||||
recommendation_params = params[:latency_recommendation]
|
||||
|
||||
offset = recommendation_params[:offset].blank?? 0 : recommendation_params[:offset]
|
||||
limit = recommendation_params[:limit].blank?? 20 : recommendation_params[:limit]
|
||||
|
||||
is_valid = [
|
||||
recommendation_params[:my_user_id],
|
||||
recommendation_params[:my_public_ip]
|
||||
].all?{ |param|
|
||||
param.present?
|
||||
}
|
||||
|
||||
if is_valid
|
||||
instruments = []
|
||||
instruments << recommendation_params[:instruments_beginner].reject(&:blank?).map{|i| { id: i, proficiency: 1} }
|
||||
instruments << recommendation_params[:instruments_advanced].reject(&:blank?).map{|i| { id: i, proficiency: 2} }
|
||||
instruments << recommendation_params[:instruments_expert].reject(&:blank?).map{|i| { id: i, proficiency: 3} }
|
||||
instruments.flatten!
|
||||
genres = recommendation_params[:genres].reject(&:blank?)
|
||||
wifi = recommendation_params[:wifi]
|
||||
max_latency = recommendation_params[:max_latency]
|
||||
|
||||
data = {
|
||||
my_user_id: recommendation_params[:my_user_id],
|
||||
my_public_ip: recommendation_params[:my_public_ip],
|
||||
offset: offset,
|
||||
limit: limit
|
||||
}
|
||||
|
||||
data.merge!({query: recommendation_params[:query]}) unless recommendation_params[:query].blank?
|
||||
data.merge!({instruments: instruments}) unless instruments.empty?
|
||||
data.merge!({genres: genres}) unless genres.empty?
|
||||
data.merge!({wifi: wifi}) if %w(true false).include?(wifi)
|
||||
data.merge!({max_latency: max_latency}) unless max_latency.blank?
|
||||
|
||||
latency_url = "#{Rails.application.config.latency_data_host}/recommendations"
|
||||
uri = URI(latency_url)
|
||||
|
||||
begin
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.read_timeout = 5
|
||||
http.use_ssl = true if Rails.application.config.latency_data_host.start_with?("https://")
|
||||
request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
||||
request.body = data.to_json
|
||||
request["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
|
||||
response = http.request(request)
|
||||
@latency_recommendation = response.body
|
||||
rescue => exception
|
||||
render text: exception.message
|
||||
end
|
||||
|
||||
else
|
||||
flash[:error] = 'Please provide all required fields'
|
||||
redirect_to admin_latency_recommendation_path
|
||||
end
|
||||
end
|
||||
|
||||
def user_latencies
|
||||
latency_params = params[:latencies]
|
||||
latency_url = "#{Rails.application.config.latency_data_host}/user_latencies"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,14 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
|
|||
|
||||
filter :jam_track
|
||||
|
||||
controller do
|
||||
def create
|
||||
jt_params = params[:jam_ruby_jam_track_right]
|
||||
jt_params[:jam_track] =JamRuby::JamTrack.where("id=?", jt_params[:jam_track_id_val]).first # jt_params[:jam_track_id_val]
|
||||
jt_params[:user] = JamRuby::User.where("id=?", jt_params[:user_id_val]).first # jt_params[:user_id_val]
|
||||
create!
|
||||
end
|
||||
end
|
||||
index do
|
||||
actions
|
||||
|
||||
|
|
@ -47,9 +55,14 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
|
|||
|
||||
form do |f|
|
||||
f.inputs 'New Jam Track Right' do
|
||||
f.input :jam_track, :required=>true, collection: JamTrack.all, include_blank: false
|
||||
f.input :user, :required=>true, collection: User.all, include_blank: false
|
||||
f.input :can_download, :required => true, as: :boolean
|
||||
#f.input :jam_track, :required=>true, collection: JamTrack.all, include_blank: false
|
||||
f.input :jam_track_id_val, :required=>true, :as => :hidden
|
||||
|
||||
f.input :jam_track, :required=>true, :as => :autocomplete, :url => autocomplete_jam_track_name_admin_jam_tracks_path, hint: 'Select a jamtrack to give to this user'
|
||||
#f.input :user, :required=>true, collection: User.all, include_blank: false
|
||||
f.input :user_id_val, :required=>true, :as => :hidden
|
||||
f.input :user, :required=>true, :as => :autocomplete, :url => autocomplete_user_email_admin_users_path, hint: 'Give a free jamtrack to this user'
|
||||
f.input :can_download, :required => true, as: :boolean, :input_html => { :checked => 'checked' }
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
|
||||
|
||||
collection_action :autocomplete_jam_track_name, :method => :get
|
||||
|
||||
menu :label => 'JamTracks', :parent => 'JamTracks'
|
||||
|
||||
config.sort_order = 'name_asc'
|
||||
|
|
@ -19,6 +21,19 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
|
|||
|
||||
form :partial => 'form'
|
||||
|
||||
|
||||
controller do
|
||||
|
||||
# this actually searches on first name, last name, and email, because of get_autocomplete_items defined below
|
||||
autocomplete :jam_track, :name, :full => true, :display_value => :autocomplete_display_name
|
||||
|
||||
def get_autocomplete_items(parameters)
|
||||
JamTrack.select("name, original_artist, id").where(["name ILIKE ? OR original_artist ILIKE ?", "%#{parameters[:term]}%", "%#{parameters[:term]}%"])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
index do
|
||||
|
||||
# actions # use this for all view/edit/delete links
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
class Spacer
|
||||
def self.spacer(val, row)
|
||||
|
||||
percentage = ((val * 100) / row.total.to_f).round(1).to_s
|
||||
('%-5.5s' % percentage).gsub(' ', ' ') + '% - ' + val.to_s
|
||||
end
|
||||
end
|
||||
=begin
|
||||
select
|
||||
count(id) as total,
|
||||
count(first_downloaded_client_at) as downloaded,
|
||||
count(first_ran_client_at) as ran_client,
|
||||
count(first_certified_gear_at) as ftue,
|
||||
count(first_music_session_at) as any_session,
|
||||
count(first_real_music_session_at) as real_session,
|
||||
count(first_good_music_session_at) as good_session,
|
||||
count(first_invited_at) as invited,
|
||||
count(first_friended_at) as friended,
|
||||
count(first_subscribed_at) as subscribed
|
||||
from users where users.created_at >= '2024-11-01' AND users.created_at < '2025-04-01'
|
||||
|
||||
select first_name, last_name, email
|
||||
from users where users.created_at >= '2024-11-01' AND users.created_at < '2025-04-01'
|
||||
AND first_music_session_at is NULL;
|
||||
|
||||
=end
|
||||
|
||||
ActiveAdmin.register_page "Jammers Subscription Cohorts" do
|
||||
menu :parent => 'Reports'
|
||||
|
||||
content :title => "Jammers Subscription Cohorts" do
|
||||
|
||||
filter_type = params[:filter_type] || 'All'
|
||||
filter_campaign = params[:filter_campaign]
|
||||
filter_campaign_id = params[:filter_campaign_id]
|
||||
filter_ad_set = params[:filter_ad_set]
|
||||
filter_ad_name = params[:filter_ad_name]
|
||||
|
||||
campaigns = User.where("origin_utm_medium = 'cpc'").distinct.pluck(:origin_utm_campaign).compact.sort
|
||||
campaign_ids = User.where("origin_utm_medium = 'cpc'").distinct.pluck(:origin_id).compact.sort
|
||||
ad_sets = User.where("origin_utm_medium = 'cpc'").distinct.pluck(:origin_term).compact.sort
|
||||
ad_names = User.where("origin_utm_medium = 'cpc'").distinct.pluck(:origin_content).compact.sort
|
||||
|
||||
div style: "margin-bottom: 20px; padding: 10px; background-color: #f4f4f4; border-radius: 4px;" do
|
||||
form action: admin_jammers_subscription_cohorts_path, method: :get do
|
||||
span "Source: ", style: "font-weight: bold; margin-right: 5px;"
|
||||
select name: 'filter_type', onchange: 'this.form.submit()', style: "margin-right: 15px;" do
|
||||
option "All", value: 'All', selected: filter_type == 'All'
|
||||
option "Organic", value: 'Organic', selected: filter_type == 'Organic'
|
||||
option "Advertising", value: 'Advertising', selected: filter_type == 'Advertising'
|
||||
end
|
||||
|
||||
if filter_type == 'Advertising'
|
||||
div style: "margin-top: 10px;" do
|
||||
span "Campaign Name: ", style: "font-weight: bold; margin-right: 5px;"
|
||||
select name: 'filter_campaign', onchange: 'this.form.submit()', style: "margin-right: 15px;" do
|
||||
option "All", value: ''
|
||||
option "Null", value: 'NULL', selected: filter_campaign == 'NULL'
|
||||
campaigns.each do |c|
|
||||
option c, value: c, selected: filter_campaign == c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
div style: "margin-top: 10px;" do
|
||||
span "Campaign ID: ", style: "font-weight: bold; margin-right: 5px;"
|
||||
select name: 'filter_campaign_id', onchange: 'this.form.submit()', style: "margin-right: 15px;" do
|
||||
option "All", value: ''
|
||||
option "Null", value: 'NULL', selected: filter_campaign_id == 'NULL'
|
||||
campaign_ids.each do |c|
|
||||
option c, value: c, selected: filter_campaign_id == c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
div style: "margin-top: 10px;" do
|
||||
span "Ad Set: ", style: "font-weight: bold; margin-right: 5px;"
|
||||
select name: 'filter_ad_set', onchange: 'this.form.submit()', style: "margin-right: 15px;" do
|
||||
option "All", value: ''
|
||||
option "Null", value: 'NULL', selected: filter_ad_set == 'NULL'
|
||||
ad_sets.each do |c|
|
||||
option c, value: c, selected: filter_ad_set == c
|
||||
end
|
||||
end
|
||||
|
||||
div style: "margin-top: 10px;" do
|
||||
span "Ad Name: ", style: "font-weight: bold; margin-right: 5px;"
|
||||
select name: 'filter_ad_name', onchange: 'this.form.submit()', style: "margin-right: 15px;" do
|
||||
option "All", value: ''
|
||||
option "Null", value: 'NULL', selected: filter_ad_name == 'NULL'
|
||||
ad_names.each do |c|
|
||||
option c, value: c, selected: filter_ad_name == c
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
noscript { input type: :submit, value: "Filter" }
|
||||
end
|
||||
end
|
||||
|
||||
h2 "Users Grouped By Month as Paying Subscribers"
|
||||
|
||||
query = User.select(%Q{date_trunc('month', users.created_at) as month,
|
||||
count(id) as total,
|
||||
count(first_downloaded_client_at) as downloaded,
|
||||
count(first_ran_client_at) as ran_client,
|
||||
count(first_certified_gear_at) as ftue,
|
||||
count(first_music_session_at) as any_session,
|
||||
count(first_real_music_session_at) as real_session,
|
||||
count(first_good_music_session_at) as good_session,
|
||||
count(first_invited_at) as invited,
|
||||
count(first_friended_at) as friended,
|
||||
count(first_subscribed_at) as subscribed,
|
||||
count(first_played_jamtrack_at) as played_jamtrack
|
||||
})
|
||||
.joins(%Q{LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
j.created_at
|
||||
FROM
|
||||
jam_track_rights as j
|
||||
WHERE
|
||||
j.user_id = users.id
|
||||
ORDER BY
|
||||
j.created_at
|
||||
LIMIT 1 -- Select only that single row
|
||||
) j ON TRUE })
|
||||
|
||||
if filter_type == 'Organic'
|
||||
query = query.where("users.origin_utm_source = 'organic'")
|
||||
elsif filter_type == 'Advertising'
|
||||
query = query.where("origin_utm_medium = 'cpc'")
|
||||
|
||||
if filter_campaign.present?
|
||||
if filter_campaign == 'NULL'
|
||||
query = query.where("users.origin_utm_campaign IS NULL")
|
||||
elsif filter_campaign != 'All'
|
||||
query = query.where("users.origin_utm_campaign = ?", filter_campaign)
|
||||
end
|
||||
end
|
||||
|
||||
if filter_campaign_id.present?
|
||||
if filter_campaign_id == 'NULL'
|
||||
query = query.where("users.origin_id IS NULL")
|
||||
elsif filter_campaign_id != 'All'
|
||||
query = query.where("users.origin_id = ?", filter_campaign_id)
|
||||
end
|
||||
end
|
||||
|
||||
if filter_ad_set.present?
|
||||
if filter_ad_set == 'NULL'
|
||||
query = query.where("users.origin_term IS NULL")
|
||||
elsif filter_ad_set != 'All'
|
||||
query = query.where("users.origin_term = ?", filter_ad_set)
|
||||
end
|
||||
end
|
||||
|
||||
if filter_ad_name.present?
|
||||
if filter_ad_name == 'NULL'
|
||||
query = query.where("users.origin_content IS NULL")
|
||||
elsif filter_ad_name != 'All'
|
||||
query = query.where("users.origin_content = ?", filter_ad_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table_for query.group("date_trunc('month', users.created_at)")
|
||||
.where("j.created_at IS NULL OR (j.created_at - users.created_at) >= INTERVAL '2 hours'")
|
||||
.order("date_trunc('month', users.created_at) DESC") do |row|
|
||||
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
||||
column "Total", :total
|
||||
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
||||
column "Downloaded", Proc.new { |user| raw(Spacer.spacer(user.downloaded, user)) }
|
||||
column "Ran Client", Proc.new { |user| raw(Spacer.spacer(user.ran_client, user)) }
|
||||
column "FTUE", Proc.new { |user| raw(Spacer.spacer(user.ftue, user)) }
|
||||
column "Any Session", Proc.new { |user| raw(Spacer.spacer(user.any_session, user)) }
|
||||
column "2+ Session", Proc.new { |user| raw(Spacer.spacer(user.real_session, user)) }
|
||||
column "Good Session", Proc.new { |user| raw(Spacer.spacer(user.good_session, user)) }
|
||||
column "Invited", Proc.new { |user| raw(Spacer.spacer(user.invited, user)) }
|
||||
column "Friended", Proc.new { |user| raw(Spacer.spacer(user.friended, user)) }
|
||||
column "Played JT", Proc.new { |user| raw(Spacer.spacer(user.played_jamtrack, user)) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
class Spacer
|
||||
def self.spacer(val, row)
|
||||
|
||||
percentage = ((val * 100) / row.total.to_f).round(1).to_s
|
||||
('%-5.5s' % percentage).gsub(' ', ' ') + '% - ' + val.to_s
|
||||
end
|
||||
end
|
||||
=begin
|
||||
select
|
||||
count(id) as total,
|
||||
count(first_downloaded_client_at) as downloaded,
|
||||
count(first_ran_client_at) as ran_client,
|
||||
count(first_certified_gear_at) as ftue,
|
||||
count(first_music_session_at) as any_session,
|
||||
count(first_real_music_session_at) as real_session,
|
||||
count(first_good_music_session_at) as good_session,
|
||||
count(first_invited_at) as invited,
|
||||
count(first_friended_at) as friended,
|
||||
count(first_subscribed_at) as subscribed
|
||||
from users where users.created_at >= '2024-11-01' AND users.created_at < '2025-04-01'
|
||||
|
||||
select first_name, last_name, email
|
||||
from users where users.created_at >= '2024-11-01' AND users.created_at < '2025-04-01'
|
||||
AND first_music_session_at is NULL;
|
||||
|
||||
=end
|
||||
|
||||
ActiveAdmin.register_page "JamTrack Subscription Cohorts" do
|
||||
menu :parent => 'Reports'
|
||||
|
||||
content :title => "JamTrack Subscription Cohorts" do
|
||||
h2 "Users Grouped By Month as Paying Subscribers"
|
||||
table_for User.select(%Q{date_trunc('month', users.created_at) as month,
|
||||
count(id) as total,
|
||||
count(first_downloaded_client_at) as downloaded,
|
||||
count(first_ran_client_at) as ran_client,
|
||||
count(first_certified_gear_at) as ftue,
|
||||
count(first_music_session_at) as any_session,
|
||||
count(first_real_music_session_at) as real_session,
|
||||
count(first_good_music_session_at) as good_session,
|
||||
count(first_invited_at) as invited,
|
||||
count(first_friended_at) as friended,
|
||||
count(first_subscribed_at) as subscribed,
|
||||
count(first_played_jamtrack_at) as played_jamtrack
|
||||
})
|
||||
.joins(%Q{INNER JOIN LATERAL (
|
||||
SELECT
|
||||
j.created_at
|
||||
FROM
|
||||
jam_track_rights as j
|
||||
WHERE
|
||||
j.user_id = users.id
|
||||
ORDER BY
|
||||
j.created_at
|
||||
LIMIT 1 -- Select only that single row
|
||||
) j ON (j.created_at - users.created_at) < INTERVAL '2 hours' })
|
||||
.group("date_trunc('month', users.created_at)").order("date_trunc('month', users.created_at) DESC") do |row|
|
||||
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
||||
column "Total", :total
|
||||
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
||||
column "Downloaded", Proc.new { |user| raw(Spacer.spacer(user.downloaded, user)) }
|
||||
column "Ran Client", Proc.new { |user| raw(Spacer.spacer(user.ran_client, user)) }
|
||||
column "FTUE", Proc.new { |user| raw(Spacer.spacer(user.ftue, user)) }
|
||||
column "Any Session", Proc.new { |user| raw(Spacer.spacer(user.any_session, user)) }
|
||||
column "2+ Session", Proc.new { |user| raw(Spacer.spacer(user.real_session, user)) }
|
||||
column "Good Session", Proc.new { |user| raw(Spacer.spacer(user.good_session, user)) }
|
||||
column "Invited", Proc.new { |user| raw(Spacer.spacer(user.invited, user)) }
|
||||
column "Friended", Proc.new { |user| raw(Spacer.spacer(user.friended, user)) }
|
||||
column "Played JT", Proc.new { |user| raw(Spacer.spacer(user.played_jamtrack, user)) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
class Spacer
|
||||
def self.spacer(val, row)
|
||||
|
||||
percentage = ((val * 100) / row.total.to_f).round(1).to_s
|
||||
('%-5.5s' % percentage).gsub(' ', ' ') + '% - ' + val.to_s
|
||||
end
|
||||
end
|
||||
|
||||
ActiveAdmin.register_page "Subscription Cohorts" do
|
||||
menu :parent => 'Reports'
|
||||
|
||||
|
||||
content :title => "Subscription Cohorts" do
|
||||
h2 "Users Grouped By Month as Paying Subscribers"
|
||||
table_for User.select(%Q{date_trunc('month', created_at) as month,
|
||||
count(id) as total,
|
||||
count(first_downloaded_client_at) as downloaded,
|
||||
count(first_ran_client_at) as ran_client,
|
||||
count(first_certified_gear_at) as ftue,
|
||||
count(first_music_session_at) as any_session,
|
||||
count(first_real_music_session_at) as real_session,
|
||||
count(first_good_music_session_at) as good_session,
|
||||
count(first_invited_at) as invited,
|
||||
count(first_friended_at) as friended,
|
||||
count(first_subscribed_at) as subscribed
|
||||
}).group("date_trunc('month', created_at)").order("date_trunc('month', created_at) DESC") do |row|
|
||||
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
||||
column "Total", :total
|
||||
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
||||
column "Downloaded", Proc.new { |user| raw(Spacer.spacer(user.downloaded, user)) }
|
||||
column "Ran Client", Proc.new { |user| raw(Spacer.spacer(user.ran_client, user)) }
|
||||
column "FTUE", Proc.new { |user| raw(Spacer.spacer(user.ftue, user)) }
|
||||
column "Any Session", Proc.new { |user| raw(Spacer.spacer(user.any_session, user)) }
|
||||
column "2+ Session", Proc.new { |user| raw(Spacer.spacer(user.real_session, user)) }
|
||||
column "Good Session", Proc.new { |user| raw(Spacer.spacer(user.good_session, user)) }
|
||||
column "Invited", Proc.new { |user| raw(Spacer.spacer(user.invited, user)) }
|
||||
column "Friended", Proc.new { |user| raw(Spacer.spacer(user.friended, user)) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
ActiveAdmin.register_page "Latency Recommendation" do
|
||||
menu parent: 'Users'
|
||||
|
||||
content :title => "Latency Recommentation" do
|
||||
render 'admin/users/latency_recommendation_form'
|
||||
end
|
||||
end
|
||||
|
|
@ -6,15 +6,18 @@ ActiveAdmin.register JamRuby::User, :as => 'UserSource' do
|
|||
config.batch_actions = false
|
||||
config.clear_action_items!
|
||||
config.filters = false
|
||||
config.per_page = 250
|
||||
|
||||
scope("Most Recent First", default: true) { |scope| scope.unscoped.order('created_at desc')}
|
||||
scope("Paid", default: true) { |scope| scope.unscoped.where(:origin_utm_medium => 'cpc').order('created_at desc') }
|
||||
scope("Inorganic Source") { |scope| scope.unscoped.where("origin_utm_source != 'organic' OR origin_utm_source IS NULL").order('created_at desc') }
|
||||
scope("Include Organic") { |scope| scope.unscoped.order('created_at desc') }
|
||||
|
||||
index do
|
||||
column "Email" do |user|
|
||||
user.email
|
||||
end
|
||||
column "Bought TestDrive" do |user|
|
||||
!user.most_recent_test_drive_purchase.nil? ? "Yes" : "No"
|
||||
column "Signup (CST)" do |user|
|
||||
user.created_at.in_time_zone("Central Time (US & Canada)")
|
||||
end
|
||||
column "UTM Source" do |user|
|
||||
user.origin_utm_source
|
||||
|
|
@ -25,8 +28,23 @@ ActiveAdmin.register JamRuby::User, :as => 'UserSource' do
|
|||
column "UTM Campaign" do |user|
|
||||
user.origin_utm_campaign
|
||||
end
|
||||
column "UTM ID" do |user|
|
||||
user.origin_id
|
||||
end
|
||||
column "UTM Term" do |user|
|
||||
user.origin_term
|
||||
end
|
||||
column "UTM Content" do |user|
|
||||
user.origin_content
|
||||
end
|
||||
column "Referrer" do |user|
|
||||
user.origin_referrer
|
||||
end
|
||||
column "FB Click ID" do |user|
|
||||
user.facebook_click_id
|
||||
end
|
||||
column "FB Browser ID" do |user|
|
||||
user.facebook_browser_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,39 +7,104 @@ function intToIP(int) {
|
|||
return part4 + "." + part3 + "." + part2 + "." + part1;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('form#user_latencies_form #latencies_my_user').on('focus', function(){
|
||||
$('#latencies_my_user_id').val('')
|
||||
function handleJamTrackRightsForm() {
|
||||
var $jamTrackRights = $('form#new_jam_ruby_jam_track_right');
|
||||
var $jamTrack = $jamTrackRights.find('#jam_ruby_jam_track_right_jam_track_id');
|
||||
var $jamTrackVal = $jamTrackRights.find('#jam_ruby_jam_track_right_jam_track_id_val')
|
||||
|
||||
var $user = $jamTrackRights.find('#jam_ruby_jam_track_right_user_id');
|
||||
var $userVal = $jamTrackRights.find('#jam_ruby_jam_track_right_user_id_val');
|
||||
$jamTrack.on('change', function(){
|
||||
console.log("change jam track");
|
||||
});
|
||||
|
||||
$('form#user_latencies_form #latencies_my_user').bind('railsAutocomplete.select', function(event, data){
|
||||
$('#latencies_my_user_id').val('');
|
||||
$('#latencies_my_public_ip').val('');
|
||||
/**
|
||||
$user.on('change', function(){
|
||||
console.log("change user");
|
||||
});
|
||||
|
||||
$jamTrack.on('focus', function(){
|
||||
$userVal.val('')
|
||||
});*/
|
||||
|
||||
$jamTrack.bind('railsAutocomplete.select', function(event, data){
|
||||
$jamTrackVal.val('');
|
||||
$jamTrackVal.val(data.item.id);
|
||||
console.log("jam track selected with id " + data.item.id);
|
||||
});
|
||||
|
||||
$user.bind('railsAutocomplete.select', function(event, data){
|
||||
$userVal.val('');
|
||||
$userVal.val(data.item.id);
|
||||
console.log("user selected with id " + data.item.id);
|
||||
});
|
||||
}
|
||||
function handleUserLatencyForm(){
|
||||
var $userLatenciesForm = $('form#user_latencies_form');
|
||||
var $latenciesMyUser = $userLatenciesForm.find('#latencies_my_user');
|
||||
var $latenciesMyUserId = $userLatenciesForm.find('#latencies_my_user_id');
|
||||
var $latenciesMyPublicIp = $userLatenciesForm.find('#latencies_my_public_ip');
|
||||
|
||||
$latenciesMyUser.on('focus', function(){
|
||||
$latenciesMyUserId.val('')
|
||||
});
|
||||
|
||||
$latenciesMyUser.bind('railsAutocomplete.select', function(event, data){
|
||||
$latenciesMyUserId.val('');
|
||||
$latenciesMyPublicIp.val('');
|
||||
if(data.item.last_jam_addr){
|
||||
var ipAddr = intToIP(data.item.last_jam_addr);
|
||||
$('#latencies_my_public_ip').val(ipAddr);
|
||||
$latenciesMyPublicIp.val(ipAddr);
|
||||
}
|
||||
$('#latencies_my_user_id').val(data.item.id);
|
||||
|
||||
$latenciesMyUserId.val(data.item.id);
|
||||
});
|
||||
|
||||
$('form#user_latencies_form #latencies_user_1').bind('railsAutocomplete.select', function(event, data){
|
||||
$('#latencies_user_1_id').val(data.item.id);
|
||||
})
|
||||
$userLatenciesForm.find('#latencies_user_1').bind('railsAutocomplete.select', function(event, data){
|
||||
$userLatenciesForm.find('#latencies_user_1_id').val(data.item.id);
|
||||
});
|
||||
|
||||
$('form#user_latencies_form #latencies_user_2').bind('railsAutocomplete.select', function(event, data){
|
||||
$('#latencies_user_2_id').val(data.item.id);
|
||||
})
|
||||
$userLatenciesForm.find('#latencies_user_2').bind('railsAutocomplete.select', function(event, data){
|
||||
$userLatenciesForm.find('#latencies_user_2_id').val(data.item.id);
|
||||
});
|
||||
|
||||
$('form#user_latencies_form #latencies_user_3').bind('railsAutocomplete.select', function(event, data){
|
||||
$('#latencies_user_3_id').val(data.item.id);
|
||||
})
|
||||
$userLatenciesForm.find('#latencies_user_3').bind('railsAutocomplete.select', function(event, data){
|
||||
$userLatenciesForm.find('#latencies_user_3_id').val(data.item.id);
|
||||
});
|
||||
|
||||
$('form#user_latencies_form #latencies_user_4').bind('railsAutocomplete.select', function(event, data){
|
||||
$('#latencies_user_4_id').val(data.item.id);
|
||||
})
|
||||
$userLatenciesForm.find('#latencies_user_4').bind('railsAutocomplete.select', function(event, data){
|
||||
$userLatenciesForm.find('#latencies_user_4_id').val(data.item.id);
|
||||
});
|
||||
|
||||
$('form#user_latencies_form #latencies_user_5').bind('railsAutocomplete.select', function(event, data){
|
||||
$('#latencies_user_5_id').val(data.item.id);
|
||||
})
|
||||
$userLatenciesForm.find('#latencies_user_5').bind('railsAutocomplete.select', function(event, data){
|
||||
$userLatenciesForm.find('#latencies_user_5_id').val(data.item.id);
|
||||
});
|
||||
}
|
||||
|
||||
function handleLatencyRecommendationForm(){
|
||||
var $latencyRecommendationForm = $('form#latency_recommendation_form');
|
||||
var $latenciesMyUser = $latencyRecommendationForm.find('#latency_recommendation_my_user');
|
||||
var $latenciesMyUserId = $latencyRecommendationForm.find('#latency_recommendation_my_user_id');
|
||||
var $latenciesMyPublicIp = $latencyRecommendationForm.find('#latency_recommendation_my_public_ip');
|
||||
|
||||
$latenciesMyUser.on('focus', function(){
|
||||
$latenciesMyUserId.val('')
|
||||
});
|
||||
|
||||
$latenciesMyUser.bind('railsAutocomplete.select', function(event, data){
|
||||
$latenciesMyUserId.val('');
|
||||
$latenciesMyPublicIp.val('');
|
||||
if(data.item.last_jam_addr){
|
||||
var ipAddr = intToIP(data.item.last_jam_addr);
|
||||
$latenciesMyPublicIp.val(ipAddr);
|
||||
}
|
||||
$latenciesMyUserId.val(data.item.id);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
handleUserLatencyForm();
|
||||
handleLatencyRecommendationForm();
|
||||
handleJamTrackRightsForm();
|
||||
});
|
||||
|
|
@ -2,55 +2,97 @@ class ArsesController < ApplicationController
|
|||
|
||||
respond_to :json
|
||||
|
||||
# create or update a client_artifact row
|
||||
def get_or_create
|
||||
name = params[:name]
|
||||
provider = params[:provider]
|
||||
active = params[:active]
|
||||
ip = params[:ip]
|
||||
username = params[:username]
|
||||
password = params[:password]
|
||||
topology = params[:topology]
|
||||
ars_id = params[:ars_id]
|
||||
puts "TOPOLOGY #{topology}"
|
||||
|
||||
if ars_id
|
||||
ars = Ars.find_by_id_int(ars_id)
|
||||
end
|
||||
if ars.nil?
|
||||
ars = Ars.new
|
||||
ars.name = name
|
||||
ars.id_int = ars_id if !ars_id.nil?
|
||||
def index
|
||||
if params[:code] != Rails.application.config.data_dump_code
|
||||
render :json => {error: "Unauthorized"}, :status => 401
|
||||
return
|
||||
end
|
||||
|
||||
ars.provider = provider
|
||||
ars.active = active
|
||||
ars.ip = ip
|
||||
ars.password = password
|
||||
ars.username = username
|
||||
if topology
|
||||
ars.city = topology['city']
|
||||
ars.country = topology['country']
|
||||
ars.continent = topology['continent']
|
||||
ars.latitude = topology['latitude']
|
||||
ars.longitude = topology['longitude']
|
||||
ars.subdivision = topology['subdivision']
|
||||
end
|
||||
ars.save
|
||||
@arses = JamRuby::Ars.all
|
||||
render :json => @arses
|
||||
end
|
||||
|
||||
@ars = ars
|
||||
unless @ars.errors.any?
|
||||
if ars_id.nil?
|
||||
ars.reload
|
||||
ars_id = ars.id_int
|
||||
def update
|
||||
if params[:code] != Rails.application.config.data_dump_code
|
||||
render :json => {error: "Unauthorized"}, :status => 401
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
# Primary ID lookup
|
||||
@ars = JamRuby::Ars.find_by_id(params[:id])
|
||||
|
||||
# Explicit secondary lookups if primary ID fails
|
||||
@ars ||= JamRuby::Ars.find_by_id_int(params[:id_int]) if params[:id_int]
|
||||
@ars ||= JamRuby::Ars.find_by_name(params[:name]) if params[:name]
|
||||
|
||||
if @ars.nil?
|
||||
render :json => {error: "Not Found"}, :status => 404
|
||||
return
|
||||
end
|
||||
|
||||
@ars = Ars.find_by_id_int(ars_id)
|
||||
render :json => {id_int: @ars.id_int, id: @ars.id, name: @ars.name, provider: @ars.provider, active: @ars.active, ip: @ars.ip}, :status => :ok
|
||||
else
|
||||
response.status = :unprocessable_entity
|
||||
respond_with @ars
|
||||
end
|
||||
allowed = [:password, :username, :active, :beta, :name, :provider, :id_int, :ip, :port, :continent, :country, :city, :subdivision, :latitude, :longitude]
|
||||
|
||||
update_hash = {}
|
||||
allowed.each do |attr|
|
||||
update_hash[attr] = params[attr] if params.has_key?(attr)
|
||||
end
|
||||
|
||||
if @ars.update_attributes(update_hash, as: :admin)
|
||||
render :json => @ars, :status => :ok
|
||||
else
|
||||
render :json => @ars.errors, :status => :unprocessable_entity
|
||||
end
|
||||
rescue => e
|
||||
render :json => {error: e.message, backtrace: e.backtrace.first(5)}, :status => 500
|
||||
end
|
||||
end
|
||||
|
||||
# create or update a client_artifact row
|
||||
def get_or_create
|
||||
begin
|
||||
name = params[:name]
|
||||
provider = params[:provider]
|
||||
active = params[:active]
|
||||
beta = params.has_key?(:beta) ? params[:beta] : true
|
||||
ip = params[:ip]
|
||||
username = params[:username]
|
||||
password = params[:password]
|
||||
topology = params[:topology]
|
||||
ars_id = params[:ars_id]
|
||||
|
||||
# Explicit field-based lookups
|
||||
ars = nil
|
||||
ars = JamRuby::Ars.find_by_id_int(ars_id) if ars_id
|
||||
ars ||= JamRuby::Ars.find_by_name(name) if name
|
||||
|
||||
if ars.nil?
|
||||
ars = JamRuby::Ars.new
|
||||
ars.name = name
|
||||
end
|
||||
|
||||
ars.id_int = ars_id if !ars_id.nil?
|
||||
ars.provider = provider
|
||||
ars.active = active
|
||||
ars.beta = params[:beta]
|
||||
ars.beta = beta
|
||||
ars.ip = ip
|
||||
ars.password = password
|
||||
ars.username = username
|
||||
if topology
|
||||
ars.city = topology['city']
|
||||
ars.country = topology['country']
|
||||
ars.continent = topology['continent']
|
||||
ars.latitude = topology['latitude']
|
||||
ars.longitude = topology['longitude']
|
||||
ars.subdivision = topology['subdivision']
|
||||
end
|
||||
ars.save!
|
||||
|
||||
@ars = ars
|
||||
render :json => {id_int: @ars.id_int, id: @ars.id, name: @ars.name, provider: @ars.provider, active: @ars.active, beta: @ars.beta, ip: @ars.ip}, :status => :ok
|
||||
rescue => e
|
||||
render :json => {error: e.message, backtrace: e.backtrace.first(5)}, :status => 500
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -112,6 +112,19 @@ class Cohort < ActiveRecord::Base
|
|||
def self.cohort_users(cohort)
|
||||
User.where(created_at: cohort.group_start..cohort.group_end)
|
||||
end
|
||||
=begin
|
||||
SELECT played.user_id FROM
|
||||
(SELECT user_id, COUNT(*) cnt FROM music_sessions_user_history msuh1
|
||||
WHERE
|
||||
msuh1.created_at >= '2024-11-01' AND
|
||||
msuh1.created_at <= '202' AND
|
||||
EXTRACT(EPOCH FROM (msuh1.session_removed_at - msuh1.created_at)) >= 900 AND
|
||||
(SELECT COUNT(*) FROM music_sessions_user_history msuh2
|
||||
WHERE msuh1.music_session_id = msuh2.music_session_id
|
||||
) > 1
|
||||
GROUP BY user_id
|
||||
) played
|
||||
=end
|
||||
|
||||
def _played_online_subquery(constraint)
|
||||
where = if constraint.is_a?(Range)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<%= semantic_form_for 'latency_recommendation', url: user_latency_recommendation_admin_users_path, html: {id: 'latency_recommendation_form'} do |f| %>
|
||||
<%= f.inputs :name => 'Select user', :class => 'inputs' do %>
|
||||
<%= f.input :my_user, :as => :autocomplete, url: autocomplete_user_email_admin_users_path, label: 'My user name/email' %>
|
||||
<%= f.input :my_user_id, as: :hidden %>
|
||||
<%= f.input :my_public_ip, label: 'My user public IP' %>
|
||||
<% end %>
|
||||
<%= f.inputs :name => 'Query parameters', :class => 'inputs' do %>
|
||||
<%= f.input :query, required: false %>
|
||||
<%= f.input :instruments_beginner, collection: JamRuby::Instrument.all, input_html: { multiple: true }, required: false %>
|
||||
<%= f.input :instruments_advanced, collection: JamRuby::Instrument.all, input_html: { multiple: true }, required: false %>
|
||||
<%= f.input :instruments_expert, collection: JamRuby::Instrument.all, input_html: { multiple: true }, required: false %>
|
||||
<%= f.input :genres, as: :select, collection: JamRuby::Genre.all, input_html: { multiple: true }, required: false %>
|
||||
<%= f.input :wifi, as: :select, collection: {'Any': 'any', 'Yes': true, 'No': false }, required: false %>
|
||||
<%= f.input :max_latency, required: false %>
|
||||
<%= f.input :offset, input_html: { value: 0 } %>
|
||||
<%= f.input :limit, input_html: { value: 20 } %>
|
||||
<% end %>
|
||||
<%= f.actions do %>
|
||||
<%= f.action :submit, :as => :button, label: 'Submit' %>
|
||||
<%= f.action :cancel, :as => :link, label: 'Cancel' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<pre><%= JSON.pretty_generate(JSON.parse(@latency_recommendation)) -%></pre>
|
||||
|
|
@ -0,0 +1 @@
|
|||
BUNDLE_GEMFILE=Gemfile.alt RAILS_ENV=development LOCAL_DEV=1 MODERN_OS=1 JAM_RUBY_VERSION=2.4.1 bundle _1.17.3_ exec rails server -b 0.0.0.0 -p 3333
|
||||
|
|
@ -131,7 +131,7 @@ module JamAdmin
|
|||
config.email_smtp_starttls_auto = true
|
||||
|
||||
config.verify_email_enabled = false
|
||||
config.musician_count = '200,000+'
|
||||
config.musician_count = '300,000+'
|
||||
config.facebook_app_id = ENV['FACEBOOK_APP_ID'] || '468555793186398'
|
||||
config.facebook_app_secret = ENV['FACEBOOK_APP_SECRET'] || '546a5b253972f3e2e8b36d9a3dd5a06e'
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
class JamRuby::JamTrackRight
|
||||
attr_accessible :jam_track, :user, :jam_track_id_val, :user_id_val, as: :admin
|
||||
|
||||
def jam_track_id_val
|
||||
|
||||
end
|
||||
|
||||
def jam_track_id_val=(val)
|
||||
end
|
||||
|
||||
def user_id_val
|
||||
|
||||
end
|
||||
|
||||
def user_id_val=(val)
|
||||
end
|
||||
end
|
||||
|
|
@ -3,4 +3,8 @@ class JamRuby::JamTrack
|
|||
# add a custom validation
|
||||
|
||||
|
||||
def autocomplete_display_name
|
||||
"#{original_artist} - #{name}"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ JamAdmin::Application.routes.draw do
|
|||
post :add_school_user, on: :collection
|
||||
patch :add_school_user, on: :member
|
||||
post :user_latencies, on: :collection
|
||||
post :user_latency_recommendation, on: :collection
|
||||
end
|
||||
|
||||
resources :jam_tracks do
|
||||
get :autocomplete_jam_track_name, :on => :collection
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -39,6 +44,8 @@ JamAdmin::Application.routes.draw do
|
|||
match '/api/jam_tracks/released' => 'jam_track#dump_released', :via => :get, as: 'released_jamtracks_csv'
|
||||
|
||||
match '/api/arses/register' => 'arses#get_or_create', :via => :post
|
||||
match '/api/arses' => 'arses#index', :via => :get
|
||||
match '/api/arses/:id' => 'arses#update', :via => :post
|
||||
|
||||
mount Resque::Server.new, :at => "/resque"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# trigger build
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
image: node:14.17.1
|
||||
image: node:14.21.3
|
||||
|
||||
pipelines:
|
||||
branches:
|
||||
|
|
@ -6,33 +6,93 @@ pipelines:
|
|||
- step:
|
||||
name: Build Staging
|
||||
script:
|
||||
- pushd jam-ui
|
||||
- npm install
|
||||
- popd
|
||||
- pushd jam-ui/cicd
|
||||
- npm install
|
||||
- NODE_ENV=production PUBLIC_URL=https://staging.jamkazam.com REACT_APP_ORIGIN=staging.jamkazam.com REACT_APP_BASE_URL=https://staging.jamkazam.com REACT_APP_CLIENT_BASE_URL=https://staging.jamkazam.com REACT_APP_API_BASE_URL=https://staging.jamkazam.com/api REACT_APP_BITBUCKET_BUILD_NUMBER=$BITBUCKET_BUILD_NUMBER REACT_APP_BITBUCKET_COMMIT=$BITBUCKET_COMMIT REACT_APP_GOOGLE_ANALYTICS_ID=G-8W0GTL53NT ENVIRONMENT=staging ./generate.sh
|
||||
- popd
|
||||
- cd jam-ui
|
||||
- npm install
|
||||
- CI=false REACT_APP_ORIGIN=staging.jamkazam.com REACT_APP_LEGACY_BASE_URL=https://staging.jamkazam.com REACT_APP_API_BASE_URL=https://staging.jamkazam.com/api REACT_APP_BITBUCKET_BUILD_NUMBER=$BITBUCKET_BUILD_NUMBER REACT_APP_BITBUCKET_COMMIT=$BITBUCKET_COMMIT npm run build
|
||||
- NODE_ENV=production CI=false PUBLIC_URL=https://staging.jamkazam.com REACT_APP_ORIGIN=staging.jamkazam.com REACT_APP_CLIENT_BASE_URL=https://staging.jamkazam.com REACT_APP_BASE_URL=https://staging.jamkazam.com REACT_APP_API_BASE_URL=https://staging.jamkazam.com/api REACT_APP_BITBUCKET_BUILD_NUMBER=$BITBUCKET_BUILD_NUMBER REACT_APP_BITBUCKET_COMMIT=$BITBUCKET_COMMIT REACT_APP_GOOGLE_ANALYTICS_ID=G-8W0GTL53NT npm run build
|
||||
artifacts:
|
||||
- jam-ui/build/**
|
||||
- step:
|
||||
name: Deploy to staging
|
||||
deployment: staging
|
||||
name: Deploy to staging - SPA
|
||||
script:
|
||||
- pipe: atlassian/aws-s3-deploy:1.1.0
|
||||
- pipe: atlassian/aws-s3-deploy:1.6.2
|
||||
variables:
|
||||
S3_BUCKET: "jamkazam-ui/stg"
|
||||
LOCAL_PATH: "jam-ui/build"
|
||||
EXTRA_ARGS: "--exclude=*backing-tracks/*"
|
||||
- step:
|
||||
name: Deploy to staging - backing-tracks
|
||||
script:
|
||||
- pipe: atlassian/aws-s3-deploy:1.6.2
|
||||
variables:
|
||||
S3_BUCKET: "jamkazam-ui/stg/backing-tracks"
|
||||
LOCAL_PATH: "jam-ui/build/backing-tracks"
|
||||
EXTRA_ARGS: "--exclude=*.js --content-type text/html"
|
||||
- step:
|
||||
name: Deploy to staging - backing-tracks js
|
||||
script:
|
||||
- pipe: atlassian/aws-s3-deploy:1.6.2
|
||||
variables:
|
||||
S3_BUCKET: "jamkazam-ui/stg/js"
|
||||
LOCAL_PATH: "jam-ui/build/js"
|
||||
EXTRA_ARGS: "--content-type text/javascript"
|
||||
# - step:
|
||||
# name: Deploy to staging - invalidate cloudfront distribution
|
||||
# deployment: staging
|
||||
# script:
|
||||
# - pipe: atlassian/aws-cloudfront-invalidate:0.10.1
|
||||
# variables:
|
||||
# DISTRIBUTION_ID: "E2AQIC9RSON94Q" # ESQDIABYLT0RV
|
||||
custom:
|
||||
build-and-deploy-to-production:
|
||||
- step:
|
||||
name: Build Production
|
||||
script:
|
||||
- pushd jam-ui
|
||||
- npm install
|
||||
- popd
|
||||
- pushd jam-ui/cicd
|
||||
- npm install
|
||||
- NODE_ENV=production ENVIRONMENT=production PUBLIC_URL=https://www.jamkazam.com REACT_APP_ORIGIN=jamkazam.com REACT_APP_BASE_URL=https://www.jamkazam.com REACT_APP_CLIENT_BASE_URL=https://www.jamkazam.com REACT_APP_API_BASE_URL=https://www.jamkazam.com/api REACT_APP_BITBUCKET_BUILD_NUMBER=$BITBUCKET_BUILD_NUMBER REACT_APP_BITBUCKET_COMMIT=$BITBUCKET_COMMIT REACT_APP_GOOGLE_ANALYTICS_ID=G-SPTNJRW7WB ./generate.sh
|
||||
- popd
|
||||
- cd jam-ui
|
||||
- npm install
|
||||
- CI=false npm run build
|
||||
- NODE_ENV=production CI=false PUBLIC_URL=https://www.jamkazam.com REACT_APP_ORIGIN=jamkazam.com REACT_APP_BASE_URL=https://www.jamkazam.com REACT_APP_CLIENT_BASE_URL=https://www.jamkazam.com REACT_APP_API_BASE_URL=https://www.jamkazam.com/api REACT_APP_BITBUCKET_BUILD_NUMBER=$BITBUCKET_BUILD_NUMBER REACT_APP_BITBUCKET_COMMIT=$BITBUCKET_COMMIT REACT_APP_GOOGLE_ANALYTICS_ID=G-SPTNJRW7WB npm run build
|
||||
|
||||
artifacts:
|
||||
- jam-ui/build/**
|
||||
|
||||
- step:
|
||||
name: Deploy to production
|
||||
deployment: production
|
||||
name: Deploy to production - SPA
|
||||
script:
|
||||
- pipe: atlassian/aws-s3-deploy:1.1.0
|
||||
- pipe: atlassian/aws-s3-deploy:1.6.2
|
||||
variables:
|
||||
S3_BUCKET: "jamkazam-ui/prd"
|
||||
LOCAL_PATH: "jam-ui/build"
|
||||
LOCAL_PATH: "jam-ui/build"
|
||||
EXTRA_ARGS: "--exclude=*backing-tracks/*"
|
||||
- step:
|
||||
name: Deploy to production - backing-tracks
|
||||
script:
|
||||
- pipe: atlassian/aws-s3-deploy:1.6.2
|
||||
variables:
|
||||
S3_BUCKET: "jamkazam-ui/prd/backing-tracks"
|
||||
LOCAL_PATH: "jam-ui/build/backing-tracks"
|
||||
EXTRA_ARGS: "--exclude=*.js --content-type text/html"
|
||||
- step:
|
||||
name: Deploy to production - backing-tracks js
|
||||
script:
|
||||
- pipe: atlassian/aws-s3-deploy:1.6.2
|
||||
variables:
|
||||
S3_BUCKET: "jamkazam-ui/prd/js"
|
||||
LOCAL_PATH: "jam-ui/build/js"
|
||||
EXTRA_ARGS: "--content-type text/javascript"
|
||||
#- step:
|
||||
# name: Deploy to production - invalidate cloudfront distribution
|
||||
# deployment: production
|
||||
# script:
|
||||
# - pipe: atlassian/aws-cloudfront-invalidate:0.10.1
|
||||
# variables:
|
||||
# DISTRIBUTION_ID: "ESQDIABYLT0RV"
|
||||
|
|
|
|||
1
build
1
build
|
|
@ -1,5 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
|
||||
# RUN_SLOW_TESTS, RUN_AWS_TESTS, SKIP_KARMA=1 SHOW_JS_ERRORS=1 PACKAGE=1
|
||||
# WORKSPACE=/var/lib/jenkins/jobs/jam-web/workspace
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
HOST=beta.jamkazam.local
|
||||
PORT=4000
|
||||
REACT_APP_ORIGIN=jamkazam.local
|
||||
REACT_APP_BASE_URL=http://beta.jamkazam.local:4000
|
||||
REACT_APP_CLIENT_BASE_URL=http://www.jamkazam.local:3000
|
||||
REACT_APP_API_BASE_URL=http://www.jamkazam.local:3000/api
|
||||
REACT_APP_BITBUCKET_BUILD_NUMBER=dev
|
||||
REACT_APP_BITBUCKET_COMMIT=dev
|
||||
REACT_APP_ENV=development
|
||||
REACT_APP_RECAPTCHA_ENABLED=false
|
||||
REACT_APP_SITE_KEY=6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID=G-MC9BTWXWY4
|
||||
PUBLIC_URL=
|
||||
REACT_APP_COOKIE_DOMAIN=.jamkazam.local
|
||||
REACT_APP_RECURLY_PUBLIC_API_KEY=ewr1-hvDV1xQxDw0HPaaRFP4KNE
|
||||
REACT_APP_BRAINTREE_TOKEN=sandbox_pgjp8dvs_5v5rwm94m2vrfbms
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
HOST=beta.jamkazam.local
|
||||
PORT=4000
|
||||
REACT_APP_ORIGIN=jamkazam.local
|
||||
REACT_APP_LEGACY_BASE_URL=http://www.jamkazam.local:3000
|
||||
REACT_APP_BASE_URL=http://beta.jamkazam.local:4000
|
||||
REACT_APP_CLIENT_BASE_URL=http://www.jamkazam.local:3000
|
||||
REACT_APP_API_BASE_URL=http://www.jamkazam.local:3000/api
|
||||
REACT_APP_BITBUCKET_BUILD_NUMBER=dev
|
||||
REACT_APP_BITBUCKET_COMMIT=dev
|
||||
REACT_APP_BITBUCKET_COMMIT=dev
|
||||
REACT_APP_ENV=development
|
||||
REACT_APP_COOKIE_DOMAIN=.jamkazam.com
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID=G-MC9BTWXWY4
|
||||
REACT_APP_RECURLY_PUBLIC_API_KEY=
|
||||
REACT_APP_BRAINTREE_TOKEN=
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
HOST=beta.jamkazam.com
|
||||
PORT=4000
|
||||
REACT_APP_ORIGIN=jamkazam.com
|
||||
REACT_APP_LEGACY_BASE_URL=https://www.jamkazam.com
|
||||
REACT_APP_API_BASE_URL=https://www.jamkazam.com/api
|
||||
REACT_APP_BASE_URL=https://www.jamkazam.com
|
||||
REACT_APP_CLIENT_BASE_URL=https://www.jamkazam.com
|
||||
REACT_APP_API_BASE_URL=https://www.jamkazam.com/api
|
||||
REACT_APP_ENV=production
|
||||
REACT_APP_RECAPTCHA_ENABLED=true
|
||||
REACT_APP_SITE_KEY=6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B
|
||||
REACT_APP_COOKIE_DOMAIN=.jamkazam.com
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID=G-SPTNJRW7WB
|
||||
REACT_APP_RECURLY_PUBLIC_API_KEY=ewr1-hvDV1xQxDw0HPaaRFP4KNE
|
||||
REACT_APP_BRAINTREE_TOKEN=production_hc7z69yq_pwwc6zm3d478kfrh
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
HOST=beta.staging.jamkazam.com
|
||||
PORT=4000
|
||||
REACT_APP_ORIGIN=staging.jamkazam.com
|
||||
REACT_APP_LEGACY_BASE_URL=https://staging.jamkazam.com
|
||||
REACT_APP_API_BASE_URL=https://staging.jamkazam.com/api
|
||||
REACT_APP_BASE_URL=https://staging.jamkazam.com
|
||||
REACT_APP_CLIENT_BASE_URL=https://staging.jamkazam.com
|
||||
REACT_APP_API_BASE_URL=https://staging.jamkazam.com/api
|
||||
REACT_APP_ENV=staging
|
||||
REACT_APP_RECAPTCHA_ENABLED=false
|
||||
REACT_APP_SITE_KEY=6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B
|
||||
REACT_APP_COOKIE_DOMAIN=.staging.jamkazam.com
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID=G-8W0GTL53NT
|
||||
REACT_APP_RECURLY_PUBLIC_API_KEY=ewr1-AjUHUfcLtIsPdtetD4mj2x
|
||||
REACT_APP_BRAINTREE_TOKEN=sandbox_pgjp8dvs_5v5rwm94m2vrfbms
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react/no-unescaped-entities": false
|
||||
"react/no-unescaped-entities": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,11 @@ yarn-error.log*
|
|||
|
||||
/.idea
|
||||
/.vscode
|
||||
/test-results
|
||||
/test-results
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
|
||||
/public/backing-tracks
|
||||
/public/js
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
v14.21.3
|
||||
|
|
@ -0,0 +1 @@
|
|||
2.7.18
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
# JamKazam new react frontend UI/UX
|
||||
|
||||
`jam-ui` is a react app created using `create-react-app` utility. to run the app on your development environment you need to `cd jam-ui` and run `npm run start`
|
||||
|
||||
The changes to the source files are auto-loaded but sometimes you might need to force close it by crtl+c and then start manually.
|
||||
|
||||
The application files goes under `src` and the react components are placed under `src/components` directory. We have a convention of naming the files using JK prefix in the filename. For example `JKMusicSessions`.
|
||||
|
||||
The routes are defined in `jam-ui/src/components/dashboard/JKDashboardMain.js`
|
||||
|
||||
|
||||
## Running react app
|
||||
|
||||
In production this React app is supposed to run on beta.jamkazam.com subdomain which is same origin domain to the production Rails app (www.jamkazam.com). This way we utilize same session based user authentication of Rails web app for authenticating users. (It looks for remember_token session cookie in headers and if it is not availale redirect the user to Rails web app sign in page)
|
||||
|
|
@ -8,7 +17,7 @@ The DOMAIN and PORT running this app is defined in env.production file. This fil
|
|||
|
||||
HOST=beta.jamkazam.local
|
||||
PORT=4000
|
||||
REACT_APP_LEGACY_BASE_URL=http://www.jamkazam.local:3000
|
||||
REACT_APP_CLIENT_BASE_URL=http://www.jamkazam.local:3000
|
||||
REACT_APP_API_BASE_URL=http://www.jamkazam.local:3000/api
|
||||
|
||||
## Subdomains setup (development)
|
||||
|
|
@ -29,3 +38,19 @@ npm run start
|
|||
|
||||
This will open it in a borwser window at http://beta.jamkazam.local:3000. Of course for it to work you also need Rails (web) app and websocket app (websocket-gateway) running.
|
||||
|
||||
## Working with JamTracks
|
||||
|
||||
if you have the latest from develop, you can go:
|
||||
```
|
||||
cd cicd
|
||||
npm install
|
||||
./export_personal_jamtracks.sh
|
||||
./generate.js
|
||||
open http://beta.jamkazam.local:4000/backing-tracks/ac-dc/back-in-black.html
|
||||
```
|
||||
|
||||
You can also do none of the above, and go straight to:
|
||||
http://beta.jamkazam.local:4000/public/backing-tracks/ac-dc/back-in-black
|
||||
|
||||
I tried to make it so the SPA has 'secret routes' to these pages, which is convenient for us dev & testing
|
||||
but also tried to make it convenient to run the cicd approach of actually generating separate pages for each landing page (which is what those 5 steps cover)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
build
|
||||
output
|
||||
node_modules
|
||||
public
|
||||
jam_track_tracks_for_jam_ui*
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
22
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"ignore": [
|
||||
"../src/components/e-commerce/*.js"
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import 'react-app-polyfill/ie9';
|
||||
import 'react-app-polyfill/stable';
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import Main from "../src/Main.js"
|
||||
import TemplatePage from "../src/components/jamtracks/JKJamTracksLandingTemplatePage.js"
|
||||
import ArtistTemplatePage from "../src/components/jamtracks/JKJamTracksArtistLandingTemplatePage.js"
|
||||
import '../src/helpers/initFA';
|
||||
import '../src/i18n/config';
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
|
||||
// Ensure props are passed correctly (or fetch from the server)
|
||||
|
||||
const props = window.jamtrack_data;
|
||||
|
||||
console.log('init', props, rootElement);
|
||||
|
||||
// Hydrate the server-rendered React component
|
||||
//ReactDOM.hydrate(React.createElement(TemplatePage, props), rootElement);
|
||||
|
||||
ReactDOM.render(
|
||||
<Main>
|
||||
{props.song ? <TemplatePage {...props} /> : <ArtistTemplatePage {...props} /> }
|
||||
</Main>, rootElement
|
||||
);
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
|
||||
# check if 1st argument spceified; if it is, then set that to SAVE_TO
|
||||
if [ -n "$1" ]; then
|
||||
SAVE_TO="$1"
|
||||
else
|
||||
SAVE_TO=/tmp
|
||||
fi
|
||||
|
||||
echo "Saving to $SAVE_TO"
|
||||
|
||||
psql jam -c "COPY( select id, original_artist, name , original_artist_slug, name_slug, plan_code, slug, allow_free, ('https://www.jamkazam.com/backing-tracks/' || original_artist_slug || '/' || name_slug) as \"URL\", (select name from jam_track_licensors l where l.id = licensor_id) as \"Licensor\", vendor_id as \"Vendor ID\" FROM jam_tracks) TO '$SAVE_TO/jam_tracks_for_jam_ui.$USER.csv' with CSV HEADER;"
|
||||
|
||||
//https://jamkazam-public.s3.amazonaws.com
|
||||
|
||||
# dump all artists
|
||||
psql jam -c "COPY( select original_artist, original_artist_slug, ('https://www.jamkazam.com/backing-tracks/' || original_artist_slug ) as \"URL\" FROM jam_tracks group by original_artist, original_artist_slug) TO '$SAVE_TO/jam_tracks_for_jam_ui_artists.$USER.csv' with CSV HEADER;"
|
||||
|
||||
psql jam -c "COPY( select id, part, instrument_id, (select description from instruments where id = instrument_id) as instrument_description, track_type, position, preview_mp3_url, preview_url as preview_ogg_url, preview_aac_url from jam_track_tracks) TO '$SAVE_TO/jam_track_tracks_for_jam_ui.$USER.csv' with CSV HEADER;"
|
||||
|
||||
echo "Moving personal files to jamtracks-for-env"
|
||||
mkdir -p jamtracks-for-env
|
||||
sudo mv $SAVE_TO/jam_tracks_for_jam_ui.$USER.csv jamtracks-for-env
|
||||
sudo mv $SAVE_TO/jam_tracks_for_jam_ui_artists.$USER.csv jamtracks-for-env
|
||||
sudo mv $SAVE_TO/jam_track_tracks_for_jam_ui.$USER.csv jamtracks-for-env
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Ensure the correct number of arguments
|
||||
if [ "$#" -lt 2 ]; then
|
||||
echo "Usage: $0 <save_to_path> <server_env>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SAVE_TO="$1"
|
||||
server_env="$2"
|
||||
|
||||
# Validate server_env
|
||||
if [ "$server_env" != "staging" ] && [ "$server_env" != "production" ]; then
|
||||
echo "Error: server_env must be either 'staging' or 'production'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine SSH target
|
||||
if [ "$server_env" == "staging" ]; then
|
||||
SSH_TARGET="jam@int.jamkazam.com"
|
||||
else
|
||||
SSH_TARGET="jam@db.jamkazam.com"
|
||||
fi
|
||||
|
||||
echo "Saving to $SAVE_TO on $server_env"
|
||||
|
||||
# Run psql commands remotely
|
||||
ssh $SSH_TARGET "psql jam -c \"COPY( select id, original_artist, name, original_artist_slug, name_slug, plan_code, slug, allow_free, ('https://www.jamkazam.com/backing-tracks/' || original_artist_slug || '/' || name_slug) as \"URL\", (select name from jam_track_licensors l where l.id = licensor_id) as \"Licensor\" FROM jam_tracks order by id::int) TO '$SAVE_TO/jam_tracks_for_jam_ui.$server_env.csv' with CSV HEADER;\""
|
||||
|
||||
ssh $SSH_TARGET "psql jam -c \"COPY( select original_artist, original_artist_slug, ('https://www.jamkazam.com/backing-tracks/' || original_artist_slug ) as \"URL\" FROM jam_tracks group by original_artist, original_artist_slug order by original_artist) TO '$SAVE_TO/jam_tracks_for_jam_ui_artists.$server_env.csv' with CSV HEADER;\""
|
||||
|
||||
ssh $SSH_TARGET "psql jam -c \"COPY( select id, part, instrument_id, (select description from instruments where id = instrument_id) as instrument_description, track_type, position, preview_mp3_url, preview_url as preview_ogg_url, preview_aac_url from jam_track_tracks order by jam_track_id::int) TO '$SAVE_TO/jam_track_tracks_for_jam_ui.$server_env.csv' with CSV HEADER;\""
|
||||
|
||||
# Move files locally from the remote server
|
||||
scp $SSH_TARGET:"$SAVE_TO/jam_tracks_for_jam_ui.$server_env.csv" jamtracks-for-env
|
||||
scp $SSH_TARGET:"$SAVE_TO/jam_tracks_for_jam_ui_artists.$server_env.csv" jamtracks-for-env
|
||||
scp $SSH_TARGET:"$SAVE_TO/jam_track_tracks_for_jam_ui.$server_env.csv" jamtracks-for-env
|
||||
|
||||
echo "Files moved successfully to local machine"
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const csv = require("csv-parser");
|
||||
const React = require("react");
|
||||
const dotenv = require("dotenv");
|
||||
const ReactDOMServer = require("react-dom/server");
|
||||
const TemplatePageModule = require("./build/components/jamtracks/JKJamTracksLandingTemplatePage");
|
||||
const ArtistTemplatePageModule = require("./build/components/jamtracks/JKJamTracksArtistLandingTemplatePage");
|
||||
|
||||
var csvFilePath = `jamtracks-for-env/jam_tracks_for_jam_ui.${process.env.USER}.csv`
|
||||
var artistCsvFilePath = `jamtracks-for-env/jam_tracks_for_jam_ui_artists.${process.env.USER}.csv`
|
||||
|
||||
var sitemapPath = path.join(__dirname, "..", "public", "sitemap.xml");
|
||||
const clear_sitemap = () => {
|
||||
fs.writeFileSync(sitemapPath, "");
|
||||
|
||||
// Add the root element
|
||||
fs.writeFileSync(sitemapPath, `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n`, { flag: 'a' });
|
||||
|
||||
// Add the root url
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}</loc></url>\n`, { flag: 'a' });
|
||||
|
||||
// Add standard URLs specific to this site, such as:
|
||||
// All prefix with /public
|
||||
|
||||
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/</loc></url>\n`, { flag: 'a' });
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/privacy</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/help</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/knowledge-base</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/help-desk</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/forum</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/unsubscribe</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/downloads</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/public/downloads-legacy</loc></url>\n`, { flag: 'a' } );
|
||||
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/auth/login</loc></url>\n`, { flag: 'a' });
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/auth/signup</loc></url>\n`, { flag: 'a' } );
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/auth/forget-password</loc></url>\n`, { flag: 'a' } );
|
||||
|
||||
// Add the closing root element
|
||||
}
|
||||
|
||||
const add_song_to_sitemap = (artistSlug, songSlug) => {
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/backing-tracks/${artistSlug}/${songSlug}</loc></url>\n`, { flag: 'a' });
|
||||
}
|
||||
|
||||
const add_artist_to_sitemap = (artistSlug) => {
|
||||
fs.writeFileSync(sitemapPath, `<url><loc>${process.env.REACT_APP_BASE_URL}/backing-tracks/${artistSlug}</loc></url>\n`, { flag: 'a' });
|
||||
}
|
||||
|
||||
const close_sitemap = () => {
|
||||
fs.writeFileSync(sitemapPath, "</urlset>", { flag: 'a' } );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a CSV file into an array of objects.
|
||||
* @param {string} csvPath - The path to the CSV file.
|
||||
* @returns {Promise<Array<Object>>} - A promise that resolves with the parsed CSV data.
|
||||
*/
|
||||
const load_csv = (csvPath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const results = [];
|
||||
fs.createReadStream(csvPath)
|
||||
.pipe(csv())
|
||||
.on('data', (data) => results.push(data))
|
||||
.on('end', () => resolve(results))
|
||||
.on('error', (error) => reject(error));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds all songs for a given artist based on the original_artist_slug
|
||||
* and sorts them alphabetically by the `slug` field.
|
||||
* @param {string} artistSlug - The original_artist_slug to match.
|
||||
* @param {Array<Object>} songsCsv - The songs CSV data.
|
||||
* @returns {Array<Object>} - A sorted array of matching song objects.
|
||||
*/
|
||||
const collect_songs_for_artist = (artistSlug, songsCsv) => {
|
||||
return songsCsv
|
||||
.filter((song) => song.original_artist_slug === artistSlug)
|
||||
.sort((a, b) => a.slug.localeCompare(b.slug)); // Sort alphabetically by slug
|
||||
};
|
||||
|
||||
|
||||
const init = () => {
|
||||
const node_env = process.env.NODE_ENV || 'development';
|
||||
const environment = process.env.ENVIRONMENT || 'development';
|
||||
console.log(`environment=${environment} node_env=${node_env}`);
|
||||
console.log(dotenv.config({ path: `../.env.${environment}` }));
|
||||
|
||||
if (environment === "production" || environment === "staging") {
|
||||
csvFilePath = `jamtracks-for-env/jam_tracks_for_jam_ui.${environment}.csv`;
|
||||
artistCsvFilePath = `jamtracks-for-env/jam_tracks_for_jam_ui_artists.${environment}.csv`;
|
||||
}
|
||||
|
||||
console.log("Song csv file", csvFilePath);
|
||||
console.log("Artist csv file", artistCsvFilePath);
|
||||
|
||||
if (!process.env.PUBLIC_URL) {
|
||||
console.log("setting public url", process.env.REACT_APP_BASE_URL);
|
||||
process.env.PUBLIC_URL = process.env.REACT_APP_BASE_URL;
|
||||
}
|
||||
|
||||
clear_sitemap();
|
||||
|
||||
|
||||
//const __dirname = path.resolve(path.dirname(''));
|
||||
|
||||
console.log("init done successfully")
|
||||
}
|
||||
|
||||
const generateSongPages = async (render) => {
|
||||
const rows = [];
|
||||
|
||||
const OUTPUT_DIR = path.join(__dirname, "..", "public", "backing-tracks");
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
console.log("generatPages starting")
|
||||
|
||||
|
||||
const TemplatePage = TemplatePageModule.default;
|
||||
|
||||
fs.createReadStream(csvFilePath)
|
||||
.pipe(csv())
|
||||
.on("data", (row) => rows.push(row))
|
||||
.on("end", async () => {
|
||||
console.log(`Processing ${rows.length} rows...`);
|
||||
|
||||
for (const row of rows) {
|
||||
// id, original_artist, name, original_artist_slug, name_slug, plan_code, slug, URL, licensor, vendor_id
|
||||
const { id, original_artist, name, original_artist_slug, name_slug, plan_code, slug, allow_free } = row;
|
||||
const artist = original_artist;
|
||||
const song = name;
|
||||
const location = `/backing-tracks/${original_artist_slug}/${name_slug}`;
|
||||
const fullPath = process.env.REACT_APP_BASE_URL + location;
|
||||
const logoPath = process.env.REACT_APP_BASE_URL + "/favicon.svg";
|
||||
|
||||
add_song_to_sitemap(original_artist_slug, name_slug);
|
||||
|
||||
console.log(`Generating ${artist} - ${song}`);
|
||||
|
||||
const html = render
|
||||
? ReactDOMServer.renderToStaticMarkup(
|
||||
React.createElement(TemplatePage, { id, plan_code, slug, artist, song, location })
|
||||
)
|
||||
: "";
|
||||
const fullHtml = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="shortcut icon" href="/favicon.svg">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${artist} - ${song} - Free Backing Track</title>
|
||||
<link rel="stylesheet" href="${process.env.REACT_APP_BASE_URL}/css/theme.css">
|
||||
<meta name="description" content="Get free ${song} by ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more">
|
||||
<meta name="keywords" content="Backing Track, ${artist}, ${song}, Instrumental">
|
||||
<meta name="author" content="JamKazam">
|
||||
|
||||
<!-- Open Graph (Facebook, LinkedIn, etc.) -->
|
||||
<meta property="og:title" content="${artist} - ${song} | Free Backing Track">
|
||||
<meta property="og:description" content="Get free ${song} by ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more">
|
||||
<meta property="og:image" content="${logoPath}">
|
||||
<meta property="og:url" content="${fullPath}">
|
||||
<meta property="og:type" content="music.song">
|
||||
|
||||
<!-- Twitter Cards -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="${artist} - ${song} | Free Backing Track">
|
||||
<meta name="twitter:description" content="Get free ${song} by ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more">
|
||||
<meta name="twitter:image" content="${logoPath}">
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href="${fullPath}" />
|
||||
|
||||
<!-- Structured Data (Schema.org) -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "MusicRecording",
|
||||
"name": "${song}",
|
||||
"byArtist": {
|
||||
"@type": "MusicGroup",
|
||||
"name": "${artist}"
|
||||
},
|
||||
"url": "${fullPath}",
|
||||
"image": "${logoPath}"
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
window.jamtrack_data = {
|
||||
id: "${id}",
|
||||
plan_code: "${plan_code}",
|
||||
slug: "${slug}",
|
||||
artist: "${artist}",
|
||||
song: "${song}",
|
||||
location: "${location}"
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">${html}</div>
|
||||
<script src="/js/client-hydrate.bundle.js"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const ARTIST_DIR = path.join(OUTPUT_DIR, original_artist_slug);
|
||||
|
||||
if (!fs.existsSync(ARTIST_DIR)) {
|
||||
fs.mkdirSync(ARTIST_DIR, { recursive: false });
|
||||
}
|
||||
|
||||
const finalOutputPath = process.env.NODE_ENV === "development" ? `${name_slug}.html` : `${name_slug}.html`;
|
||||
const outputFilePath = path.join(ARTIST_DIR, finalOutputPath);
|
||||
fs.writeFileSync(outputFilePath, fullHtml);
|
||||
console.log(`Generated: ${outputFilePath}`);
|
||||
}
|
||||
|
||||
console.log("All pages generated!");
|
||||
});
|
||||
};
|
||||
|
||||
const generateArtistPages = async (render) => {
|
||||
const rows = [];
|
||||
|
||||
const OUTPUT_DIR = path.join(__dirname, "..", "public", "backing-tracks");
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
console.log("generatPages starting")
|
||||
|
||||
|
||||
const TemplatePage = TemplatePageModule.default;
|
||||
|
||||
const songs_csv = await load_csv(csvFilePath);
|
||||
|
||||
fs.createReadStream(artistCsvFilePath)
|
||||
.pipe(csv())
|
||||
.on("data", (row) => rows.push(row))
|
||||
.on("end", async () => {
|
||||
console.log(`Processing ${rows.length} rows...`);
|
||||
|
||||
for (const row of rows) {
|
||||
const { original_artist, original_artist_slug, url } = row;
|
||||
const artist = original_artist;
|
||||
|
||||
const matchingSongs = collect_songs_for_artist(original_artist_slug, songs_csv);
|
||||
console.log(`Found ${matchingSongs.length} songs for ${artist}`);
|
||||
|
||||
const location = `/backing-tracks/${original_artist_slug}`;
|
||||
const fullPath = process.env.REACT_APP_BASE_URL + location;
|
||||
const logoPath = process.env.REACT_APP_BASE_URL + "/favicon.svg";
|
||||
|
||||
add_artist_to_sitemap(original_artist_slug);
|
||||
console.log(`Generating ${artist}`);
|
||||
|
||||
const songs = matchingSongs.map((song) => {
|
||||
return {
|
||||
name: song.name,
|
||||
plan_code: song.plan_code,
|
||||
url: process.env.REACT_APP_BASE_URL + "/backing-tracks/" + song.original_artist_slug + "/" + song.name_slug
|
||||
}
|
||||
});
|
||||
const html = render
|
||||
? ReactDOMServer.renderToStaticMarkup(
|
||||
React.createElement(ArtistTemplatePage, { artist, original_artist_slug, location })
|
||||
)
|
||||
: "";
|
||||
const fullHtml = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="shortcut icon" href="/favicon.svg">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${artist} - Free Backing Track</title>
|
||||
<link rel="stylesheet" href="${process.env.REACT_APP_BASE_URL}/css/theme.css">
|
||||
<meta name="description" content="Get a free ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more">
|
||||
<meta name="keywords" content="Backing Track, ${artist}, Instrumental">
|
||||
<meta name="author" content="JamKazam">
|
||||
|
||||
<!-- Open Graph (Facebook, LinkedIn, etc.) -->
|
||||
<meta property="og:title" content="${artist} | Free Backing Track">
|
||||
<meta property="og:description" content="Get a free ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more">
|
||||
<meta property="og:image" content="${logoPath}">
|
||||
<meta property="og:url" content="${fullPath}">
|
||||
<meta property="og:type" content="music.song">
|
||||
|
||||
<!-- Twitter Cards -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="${artist} | Free Backing Track">
|
||||
<meta name="twitter:description" content="Get a free ${artist} backing track, plus free tools to mute any part, slow down for practice, record yourself, more">
|
||||
<meta name="twitter:image" content="${logoPath}">
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href="${fullPath}" />
|
||||
|
||||
<script>
|
||||
window.jamtrack_data = {
|
||||
artist: "${artist}",
|
||||
original_artist_slug: "${original_artist_slug}",
|
||||
location: "${location}",
|
||||
songs: ${JSON.stringify(songs)}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">${html}</div>
|
||||
<script src="/js/client-hydrate.bundle.js"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const finalOutputPath = process.env.NODE_ENV === "development" ? `${original_artist_slug}.html` : `${original_artist_slug}.html`;
|
||||
const outputFilePath = path.join(OUTPUT_DIR, finalOutputPath);
|
||||
fs.writeFileSync(outputFilePath, fullHtml);
|
||||
console.log(`Generated: ${outputFilePath}`);
|
||||
}
|
||||
|
||||
close_sitemap();
|
||||
|
||||
console.log("All pages generated!");
|
||||
});
|
||||
};
|
||||
|
||||
let render = false;
|
||||
if (process.argv.length > 2) {
|
||||
render = process.argv[2] === "true" || process.argv[2] === "yes" || process.argv[2] === "1";
|
||||
}
|
||||
|
||||
init()
|
||||
generateSongPages(render);
|
||||
|
||||
generateArtistPages(render);
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#!/bin/bash
|
||||
|
||||
# requires node 22 at least
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
# default NODE_ENV to development if not set
|
||||
if [ -z "$NODE_ENV" ]; then
|
||||
NODE_ENV=development
|
||||
fi
|
||||
|
||||
# default ENVIRONMENT to development if not set
|
||||
if [ -z "$ENVIRONMENT" ]; then
|
||||
ENVIRONMENT=development
|
||||
fi
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
export NODE_ENV
|
||||
export ENVIRONMENT
|
||||
|
||||
echo "cleaning"
|
||||
rm -rf $SCRIPT_DIR/build
|
||||
|
||||
echo "creating build dir - this is for correct image resolution; ideally this wouldn't be needed"
|
||||
|
||||
#mkdir -p $SCRIPT_DIR/build/assets/img
|
||||
#cp -r $SCRIPT_DIR/../src/assets/img/* $SCRIPT_DIR/build/assets/img/
|
||||
|
||||
echo "creating client-hydrate.bundle.js for jamtrack landing pages"
|
||||
# PUBLIC_URL=? for server builds
|
||||
# NODE_ENV=production for server builds
|
||||
npx webpack
|
||||
#cp -r public/* output/
|
||||
|
||||
|
||||
npm run build
|
||||
|
||||
echo "run generate.js for all jamtracks defined in the CSV"
|
||||
NODE_ENV=$NODE_ENV ENVIRONMENT=$ENVIRONMENT npm run generate-song-landing-pages
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "react-static-site-generator",
|
||||
"version": "1.0.0",
|
||||
"description": "A React-based static site generator from CSV",
|
||||
"main": "generate.js",
|
||||
"dependencies": {
|
||||
"csv-parser": "^3.0.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"generate-song-landing-pages": "node generate.js",
|
||||
"build": "babel ../src/ --out-dir build/",
|
||||
"webpack": "webpack"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.26.4",
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@babel/register": "^7.25.9",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"babel-loader": "^9.2.1",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
"image-minimizer-webpack-plugin": "^4.1.3",
|
||||
"imagemin": "^9.0.0",
|
||||
"webpack": "^5.98.0",
|
||||
"webpack-cli": "^6.0.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
const path = require("path");
|
||||
const webpack = require('webpack');
|
||||
const Dotenv = require("dotenv-webpack");
|
||||
|
||||
const environment = process.env.ENVIRONMENT || 'development';
|
||||
const node_env = process.env.NODE_ENV || 'development';
|
||||
const debug = node_env == 'development';
|
||||
module.exports = {
|
||||
entry: "./client-hydrate.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "..", "public", "js"),
|
||||
filename: "client-hydrate.bundle.js",
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
'@babel/preset-react'
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.svg/,
|
||||
type: 'asset/inline'
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif)$/i,
|
||||
type: 'asset',
|
||||
}
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx"],
|
||||
alias: {
|
||||
react: path.resolve('./node_modules/react'),
|
||||
}
|
||||
},
|
||||
devtool: debug ? 'eval-source-map' : 'source-map',
|
||||
mode: node_env,
|
||||
plugins: [
|
||||
new Dotenv({
|
||||
path: `../.env.${environment}`
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
minimize: node_env === 'production', //only minimize in production
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
legacyBaseUrl: "http://www.jamkazam.local:3000",
|
||||
apiBaseUrl: "http://www.jamkazam.local:3000/api",
|
||||
},
|
||||
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
return require("./cypress/plugins/index.js")(on, config);
|
||||
},
|
||||
baseUrl: "http://beta.jamkazam.local:4000",
|
||||
},
|
||||
|
||||
component: {
|
||||
devServer: {
|
||||
framework: "react",
|
||||
bundler: "webpack",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"baseUrl": "http://beta.jamkazam.local:4000",
|
||||
"env": {
|
||||
"legacyBaseUrl": "http://www.jamkazam.local:3000",
|
||||
"apiBaseUrl": "http://www.jamkazam.local:3000/api"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Change Current Email/Password Feature', () => {
|
||||
beforeEach(() => {
|
||||
// Log in to the application or navigate to the account page
|
||||
// where the change email feature is available
|
||||
const currentUser = makeFakeUser({
|
||||
email: 'sam@example.com'
|
||||
});
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.visit('/account/identity');
|
||||
});
|
||||
|
||||
describe('Email update', () => {
|
||||
it('should display the current email address', () => {
|
||||
// Assert that the current email address is displayed
|
||||
cy.get('[data-testid=edit_email_form]').within($form => {
|
||||
cy.get('input#new_email[placeholder="sam@example.com"]');
|
||||
});
|
||||
});
|
||||
|
||||
it('shows validation error if no data is entered', () => {
|
||||
// Assert that an error message is displayed
|
||||
cy.get('[data-testid=edit_email_form]').within($form => {
|
||||
cy.get('[data-testid=email_submit]').click();
|
||||
cy.contains('Current password is required');
|
||||
cy.contains('New Email is required');
|
||||
});
|
||||
});
|
||||
|
||||
it('shows validation error if new email is invalid', () => {
|
||||
cy.get('[data-testid=edit_email_form]').within($form => {
|
||||
// Enter the current password
|
||||
cy.get('input#current_password').type('password');
|
||||
// Enter an invalid email address in the input field
|
||||
cy.get('input#new_email').type('invalidemail');
|
||||
|
||||
cy.get('[data-testid=email_submit]').click();
|
||||
// Assert that an error message is displayed
|
||||
cy.contains('Email is invalid');
|
||||
});
|
||||
});
|
||||
|
||||
it('reveal password', () => {
|
||||
cy.get('[data-testid=edit_email_form]').within($form => {
|
||||
// Click on the reveal password button
|
||||
cy.get('.input-group-append').click();
|
||||
// Assert that the password is revealed
|
||||
cy.get('input#current_password').should('have.attr', 'type', 'text');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user submits the form', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', /\S+\/users\S+/, {
|
||||
statusCode: 200
|
||||
}).as('updateEmail');
|
||||
});
|
||||
|
||||
it('should update the email address', () => {
|
||||
cy.get('[data-testid=edit_email_form]').within($form => {
|
||||
// Enter the current password
|
||||
cy.get('input#current_password').type('password');
|
||||
// Enter the new email address
|
||||
cy.get('input#new_email').type('samsam@example.com');
|
||||
|
||||
// Submit the form
|
||||
cy.get('[data-testid=email_submit]').click();
|
||||
});
|
||||
|
||||
// Assert that the email address is updated
|
||||
cy.contains('A confirmation email has been sent to your email address');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Password update', () => {
|
||||
it('shows validation error if no data is entered', () => {
|
||||
// Assert that an error message is displayed
|
||||
cy.get('[data-testid=edit_password_form]').within($form => {
|
||||
cy.get('[data-testid=password_submit]').click();
|
||||
cy.contains('Current password is required');
|
||||
cy.contains('New password is required');
|
||||
});
|
||||
});
|
||||
|
||||
it('reveal password', () => {
|
||||
cy.get('[data-testid=edit_password_form]').within($form => {
|
||||
// Click on the reveal password button
|
||||
cy.get('.current-password-reveal').click();
|
||||
// Assert that the password is revealed
|
||||
cy.get('input#current_password').should('have.attr', 'type', 'text');
|
||||
|
||||
// Click on the reveal password button
|
||||
cy.get('.new-password-reveal').click();
|
||||
// Assert that the password is revealed
|
||||
cy.get('input#new_password').should('have.attr', 'type', 'text');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Submit form with errors', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', /\S+\/users\S+\/set_password/, {
|
||||
statusCode: 422,
|
||||
body: {
|
||||
errors: {
|
||||
password: ['is too short']
|
||||
}
|
||||
}
|
||||
}).as('updatePassword');
|
||||
});
|
||||
|
||||
it('shows validation error if new password is invalid', () => {
|
||||
cy.get('[data-testid=edit_password_form]').within($form => {
|
||||
// Enter the current password
|
||||
cy.get('input#current_password').type('password');
|
||||
// Enter an invalid new password
|
||||
cy.get('input#new_password').type('short');
|
||||
|
||||
cy.get('[data-testid=password_submit]').click();
|
||||
// Assert that an error message is displayed
|
||||
cy.contains('New Password is too short');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Submit with valid data', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', /\S+\/users\S+\/set_password/, {
|
||||
statusCode: 200
|
||||
}).as('updatePassword');
|
||||
});
|
||||
|
||||
it('should update the password', () => {
|
||||
cy.get('[data-testid=edit_password_form]').within($form => {
|
||||
// Enter the current password
|
||||
cy.get('input#current_password').type('password');
|
||||
// Enter the new password
|
||||
cy.get('input#new_password').type('newpassword');
|
||||
|
||||
// Submit the form
|
||||
cy.get('[data-testid=password_submit]').click();
|
||||
});
|
||||
|
||||
// Assert that the password is updated
|
||||
cy.contains('password has been successfully updated');
|
||||
});
|
||||
})
|
||||
|
||||
describe('Forgot password', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', /\S+\/users\S+\/request_reset_password/, {
|
||||
statusCode: 200
|
||||
}).as('forgotPassword');
|
||||
});
|
||||
|
||||
it('sends forgot password email', () => {
|
||||
cy.get('[data-testid=forgot_password]').click();
|
||||
cy.contains('A password reset email has been sent to your email');
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Account Preferences Feature', () => {
|
||||
beforeEach(() => {
|
||||
// Log in to the application or navigate to the account page
|
||||
// where the change email feature is available
|
||||
const currentUser = makeFakeUser({
|
||||
email: 'sam@example.com',
|
||||
recording_pref: 0
|
||||
});
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.intercept('POST', /\S+\/users\S+/, {
|
||||
statusCode: 200
|
||||
}).as('updateUser');
|
||||
cy.visit('/account/preferences');
|
||||
});
|
||||
|
||||
it('should display the current recording preference', () => {
|
||||
// Assert that the current recording preference is displayed
|
||||
cy.get('[data-testid=recording_pref_none]').should('be.checked');
|
||||
});
|
||||
|
||||
it('should save the new recording preference', () => {
|
||||
// Select a new recording preference
|
||||
cy.get('[data-testid=recording_pref_my_audio]').check('1');
|
||||
cy.wait('@updateUser');
|
||||
// Assert that the new recording preference is saved
|
||||
cy.contains('Recording preference saved successfully.');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('User subscribe to a plan', () => {
|
||||
beforeEach(() => {
|
||||
// Log in to the application or navigate to the account page
|
||||
// where the change email feature is available
|
||||
const currentUser = makeFakeUser({
|
||||
email: 'sam@example.com'
|
||||
});
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.stubGonSubscriptionCodes();
|
||||
|
||||
|
||||
cy.intercept('GET', /\S+\/get_subscription/, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
past_due: false,
|
||||
has_billing_info: true,
|
||||
plan_code: 'jamsubsilver',
|
||||
desired_plan_code: 'jamsubgold',
|
||||
admin_overide_plan_code: null,
|
||||
admin_overide_ends_at: null,
|
||||
in_trial: false,
|
||||
trial_ends_at: null,
|
||||
subscription: {
|
||||
|
||||
},
|
||||
subscription_rules: {
|
||||
remaining_monthly_play_time: null
|
||||
}
|
||||
}
|
||||
}).as('getSubscription');
|
||||
|
||||
cy.intercept('GET', /\S+\/get_subscription/, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
past_due: false,
|
||||
has_billing_info: true,
|
||||
plan_code: '',
|
||||
desired_plan_code: '',
|
||||
admin_overide_plan_code: null,
|
||||
admin_overide_ends_at: null,
|
||||
in_trial: false,
|
||||
trial_ends_at: null,
|
||||
subscription: {
|
||||
|
||||
},
|
||||
subscription_rules: {
|
||||
remaining_monthly_play_time: null
|
||||
}
|
||||
}
|
||||
}).as('getSubscriptionWithNoPlan');
|
||||
|
||||
});
|
||||
|
||||
it('should display the current plan', () => {
|
||||
cy.visit('/account/subscription');
|
||||
cy.wait('@getSubscription')
|
||||
// Assert that the current plan is displayed
|
||||
cy.get('[data-testid=changeSubscriptionForm]').within($form => {
|
||||
cy.get('label').contains('Subscription Plan');
|
||||
cy.get('.select-plan__single-value').should('contain', 'Gold ($9.99/month)');
|
||||
cy.get('input[type=submit]').should('have.value', 'Save Plan').should('be.disabled');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=playtime]').within($playtime => {
|
||||
cy.get('label').contains('You have unlimited play time');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=subscription-explanation]').within($expl => {
|
||||
cy.get('.alert').contains('You are currently on the Silver (monthly) level, thank you!');
|
||||
});
|
||||
});
|
||||
|
||||
it.only('should display the current plan when no plan', () => {
|
||||
cy.visit('/account/subscription');
|
||||
cy.wait('@getSubscriptionWithNoPlan')
|
||||
// Assert that the current plan is displayed
|
||||
cy.get('[data-testid=changeSubscriptionForm]').within($form => {
|
||||
cy.get('label').contains('Subscription Plan');
|
||||
cy.get('.select-plan__single-value').should('contain', 'Free ($0.00/month)');
|
||||
cy.get('input[type=submit]').should('have.value', 'Save Plan').should('be.disabled');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=playtime]').within($playtime => {
|
||||
cy.get('label').contains('You have unlimited play time.');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=subscription-explanation]').within($expl => {
|
||||
cy.get('.alert').contains('You are currently on the Free (monthly) plan.');
|
||||
});
|
||||
});
|
||||
|
||||
it('change plan', () => {
|
||||
cy.visit('/account/subscription');
|
||||
cy.wait('@getSubscription')
|
||||
cy.get('[data-testid=changeSubscriptionForm]').within($form => {
|
||||
cy.get('.select-plan__control').click();
|
||||
cy.get('.select-plan__option').contains('Platinum ($19.99/month)').click();
|
||||
cy.get('input[type=submit]').should('be.enabled').click();
|
||||
});
|
||||
|
||||
const text = 'You have selected the PLATINUM MONTHLY PLAN and will be charged US$19.99'
|
||||
cy.contains('.modal-body', text).should('be.visible');
|
||||
cy.get('.modal-content').find('.modal-footer').contains('OK').click();
|
||||
|
||||
cy.get('.modal-body').should('not.be.visible');
|
||||
// cy.get('[data-testid=subscription-explanation]').within($expl => {
|
||||
// cy.get('.alert').contains('You are currently on the Silver (monthly) level, thank you! And your plan and billing will switch to the Platinum (monthly) level on the next billing cycle.');
|
||||
// });
|
||||
cy.get('input[type=submit]').should('be.disabled')
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/// <reference types="cypress" />
|
||||
import makeFakeUser from '../../factories/user';
|
||||
describe('Change Email Confirm Page', () => {
|
||||
beforeEach(() => {
|
||||
// Log in to the application or navigate to the account page
|
||||
// where the change email feature is available
|
||||
const currentUser = makeFakeUser({
|
||||
email: 'sam@example.com'
|
||||
});
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.intercept('POST', /\S+\/update_email/, { statusCode: 200, body: { ok: true } });
|
||||
});
|
||||
|
||||
it('should display the confirm page when visiting the confirm URL', () => {
|
||||
// Replace with a realistic token for your app if needed
|
||||
const token = 'dummy-confirm-token';
|
||||
|
||||
// Visit the confirm URL
|
||||
cy.visit(`/confirm-email-change?token=${token}`);
|
||||
|
||||
// Assert that the JKChangeEmailConfirm page is rendered
|
||||
// Adjust selectors/texts as per your actual component
|
||||
cy.contains('Change Email Confirmation').should('be.visible');
|
||||
cy.contains('Loading...').should('be.visible');
|
||||
// Optionally, check for the success message after the email update
|
||||
cy.wait(1000); // Wait for the email update to complete
|
||||
cy.contains('Your email has been successfully updated.').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Profile update', () => {
|
||||
beforeEach(() => {
|
||||
// Log in to the application or navigate to the account page
|
||||
// where the change email feature is available
|
||||
const currentUser = makeFakeUser();
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.intercept('GET', /\S+\/users\/\S+\/profile/, {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
first_name: 'David',
|
||||
last_name: 'Wilson',
|
||||
name: 'David Wilson',
|
||||
city: 'Barstow',
|
||||
state: 'CA',
|
||||
country: 'US',
|
||||
location: 'Barstow, CA',
|
||||
photo_url: null,
|
||||
biography: 'This is the musician biography. It is a long form text.',
|
||||
virtual_band: true,
|
||||
virtual_band_commitment: 2,
|
||||
traditional_band: false,
|
||||
traditional_band_commitment: null,
|
||||
traditional_band_touring: false,
|
||||
paid_sessions: true,
|
||||
paid_sessions_hourly_rate: 19900,
|
||||
paid_sessions_daily_rate: 200000,
|
||||
free_sessions: true,
|
||||
cowriting: true,
|
||||
cowriting_purpose: 2,
|
||||
subscribe_email: true,
|
||||
genres: [
|
||||
{
|
||||
genre_id: 'asian',
|
||||
player_type: 'JamRuby::User',
|
||||
genre_type: 'profile'
|
||||
},
|
||||
{
|
||||
genre_id: 'blues',
|
||||
player_type: 'JamRuby::User',
|
||||
genre_type: 'profile'
|
||||
},
|
||||
{
|
||||
genre_id: 'dance',
|
||||
player_type: 'JamRuby::User',
|
||||
genre_type: 'profile'
|
||||
}
|
||||
],
|
||||
instruments: [
|
||||
{
|
||||
description: 'Acoustic Guitar',
|
||||
proficiency_level: 3,
|
||||
priority: 0,
|
||||
instrument_id: 'acoustic guitar'
|
||||
},
|
||||
{
|
||||
description: 'Bass Guitar',
|
||||
proficiency_level: 3,
|
||||
priority: 2,
|
||||
instrument_id: 'bass guitar'
|
||||
},
|
||||
{
|
||||
description: 'Violin',
|
||||
proficiency_level: 1,
|
||||
priority: 0,
|
||||
instrument_id: 'violin'
|
||||
},
|
||||
{
|
||||
description: 'Banjo',
|
||||
proficiency_level: 1,
|
||||
priority: 1,
|
||||
instrument_id: 'banjo'
|
||||
}
|
||||
]
|
||||
}
|
||||
}).as('getProfile');
|
||||
|
||||
cy.intercept('PUT', /\S+\/users\/\S+/, {
|
||||
statusCode: 200
|
||||
}).as('updateProfile');
|
||||
|
||||
cy.visit('/profile');
|
||||
});
|
||||
|
||||
it('should render the profile form with persisted data', () => {
|
||||
// Assert that the profile form is rendered
|
||||
cy.wait('@getProfile');
|
||||
cy.get('[data-testid=edit_profile_form]').within($form => {
|
||||
cy.get('[data-testid=firstName]').should('have.value', 'David');
|
||||
cy.get('[data-testid=lastName]').should('have.value', 'Wilson');
|
||||
cy.get('[data-testid=biography]').should('contain', 'This is the musician biography. It is a long form text');
|
||||
|
||||
cy.get('[data-testid=subscribeEmail]').should('be.checked');
|
||||
cy.get('[data-testid=virtualBand]').should('be.checked');
|
||||
cy.get('[data-testid=traditionalBand]').should('not.be.checked');
|
||||
cy.get('[data-testid=cowriting]').should('be.checked');
|
||||
cy.get('[data-testid=instruments] input:checked').should('have.length', 4);
|
||||
cy.get('[data-testid=genres] input:checked').should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
||||
it.only('should update the profile', () => {
|
||||
// Update the profile form
|
||||
|
||||
cy.get('[data-testid=edit_profile_form]').within($form => {
|
||||
// Update the first name
|
||||
cy.get('[data-testid=firstName]')
|
||||
.clear()
|
||||
.type('Seth');
|
||||
|
||||
// Update the last name
|
||||
cy.get('[data-testid=lastName]')
|
||||
.clear()
|
||||
.type('Call');
|
||||
|
||||
cy.wait(2000);
|
||||
});
|
||||
|
||||
cy.reload();
|
||||
|
||||
cy.get('[data-testid=firstName]').should('have.value', 'Seth');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Payments Page', () => {
|
||||
beforeEach(() => {
|
||||
// Place any setup code here, such as logging in or navigating to the payments page
|
||||
const currentUser = makeFakeUser();
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.intercept('GET', /\S+\/invoice_history\?limit=10&cursor=0/, { fixture: 'payments_page1' }).as('getPayments1');
|
||||
cy.intercept('GET', /\S+\/invoice_history\?limit=10&cursor=10/, { fixture: 'payments_page2' }).as('getPayments2');
|
||||
cy.intercept('GET', /\S+\/invoice_history\?limit=10&cursor=20/, { fixture: 'payments_page3' }).as('getPayments3');
|
||||
});
|
||||
|
||||
describe('in desktop', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport('macbook-13');
|
||||
});
|
||||
|
||||
it('should list user\'s payments', () => {
|
||||
// Call the external API that fetches the payment records
|
||||
cy.visit('/account/payments')
|
||||
cy.wait('@getPayments1')
|
||||
cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr td').first().within(() => {
|
||||
cy.contains('12/01/2022')
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it('paginate through the payments', () => {
|
||||
// Call the external API that fetches the payment records
|
||||
cy.visit('/account/payments')
|
||||
cy.wait('@getPayments1')
|
||||
cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr').first().within(() => {
|
||||
cy.contains('12/01/2022')
|
||||
})
|
||||
|
||||
// Click the next page button
|
||||
cy.get('[data-testid="nextPageButton"]').click()
|
||||
cy.wait('@getPayments2')
|
||||
cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr:nth-child(11)').first().within(() => {
|
||||
cy.contains('02/01/2022')
|
||||
})
|
||||
|
||||
// Click the next page button
|
||||
cy.get('[data-testid="nextPageButton"]').click()
|
||||
cy.wait('@getPayments3')
|
||||
cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr:nth-child(22)').first().within(() => {
|
||||
cy.contains('03/01/2021')
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('in mobile', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport('iphone-6');
|
||||
});
|
||||
|
||||
it('should list user\'s payments', () => {
|
||||
// Call the external API that fetches the payment records
|
||||
cy.visit('/account/payments')
|
||||
cy.wait('@getPayments1')
|
||||
cy.get('[data-testid=paymentSwiper]').should('be.visible')
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
cy.get('.swiper-button-next').click();
|
||||
cy.wait(500);
|
||||
}
|
||||
cy.wait('@getPayments2')
|
||||
for (let i = 0; i < 10; i++) {
|
||||
cy.get('.swiper-button-next').click();
|
||||
cy.wait(500);
|
||||
}
|
||||
cy.get('.swiper-button-next').click();
|
||||
cy.wait('@getPayments3')
|
||||
for (let i = 0; i < 2; i++) {
|
||||
cy.get('.swiper-button-next').click();
|
||||
cy.wait(500);
|
||||
}
|
||||
cy.get('.swiper-button-next').should('have.class', 'swiper-button-disabled');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Affiliate Earnings', () => {
|
||||
beforeEach(() => {
|
||||
const currentUser = makeFakeUser();
|
||||
cy.stubAuthenticate({ id: currentUser.id });
|
||||
cy.stubGonSubscriptionCodes();
|
||||
});
|
||||
|
||||
describe('When user has no earnings', () => {
|
||||
it('should show no earnings message', () => {
|
||||
cy.intercept('GET', /\S+\/api\/affiliate_partners\/payments/, {
|
||||
body: [
|
||||
{
|
||||
payments: [],
|
||||
next: null
|
||||
}
|
||||
]
|
||||
}).as('fetchAllEarnings');
|
||||
cy.visit('/affiliate/earnings');
|
||||
cy.wait('@fetchAllEarnings');
|
||||
cy.contains('There is no earnings data yet ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When user has earnings', () => {
|
||||
it('should show earnings', () => {
|
||||
cy.intercept('GET', /\S+\/api\/affiliate_partners\/payments/, {
|
||||
body: {
|
||||
payments: [
|
||||
{
|
||||
payment_type: 'monthly',
|
||||
year: 2021,
|
||||
month: 9,
|
||||
subscriptions: [
|
||||
{
|
||||
plan: 'jamsubsilver',
|
||||
count: 2
|
||||
}
|
||||
],
|
||||
jamtracks_sold: 3,
|
||||
due_amount_in_cents: 5000
|
||||
},
|
||||
{
|
||||
payment_type: 'monthly',
|
||||
year: 2021,
|
||||
month: 8,
|
||||
subscriptions: [
|
||||
{
|
||||
plan: 'jamsubgold',
|
||||
count: 1
|
||||
},
|
||||
{
|
||||
plan: 'jamsubplatinum',
|
||||
count: 2
|
||||
}
|
||||
],
|
||||
jamtracks_sold: 2,
|
||||
due_amount_in_cents: 3000
|
||||
}
|
||||
],
|
||||
next: null
|
||||
}
|
||||
}).as('fetchAllEarnings');
|
||||
cy.visit('/affiliate/earnings');
|
||||
cy.wait('@fetchAllEarnings');
|
||||
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
|
||||
.eq(0)
|
||||
.should('have.text', 'September 2021');
|
||||
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
|
||||
.eq(1)
|
||||
.should('have.text', 'Silver - 2');
|
||||
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
|
||||
.eq(2)
|
||||
.should('have.text', '3');
|
||||
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
|
||||
.eq(3)
|
||||
.should('have.text', '$50.00');
|
||||
});
|
||||
});
|
||||
|
||||
describe('in mobile', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('iphone-6');
|
||||
});
|
||||
|
||||
it('should show earnings', () => {
|
||||
cy.intercept('GET', /\S+\/api\/affiliate_partners\/payments/, {
|
||||
body: {
|
||||
payments: [
|
||||
{
|
||||
payment_type: 'monthly',
|
||||
year: 2021,
|
||||
month: 9,
|
||||
subscriptions: [
|
||||
{
|
||||
plan: 'jamsubsilver',
|
||||
count: 2
|
||||
}
|
||||
],
|
||||
jamtracks_sold: 3,
|
||||
due_amount_in_cents: 5000
|
||||
},
|
||||
{
|
||||
payment_type: 'monthly',
|
||||
year: 2021,
|
||||
month: 8,
|
||||
subscriptions: [
|
||||
{
|
||||
plan: 'jamsubgold',
|
||||
count: 1
|
||||
},
|
||||
{
|
||||
plan: 'jamsubplatinum',
|
||||
count: 2
|
||||
}
|
||||
],
|
||||
jamtracks_sold: 2,
|
||||
due_amount_in_cents: 3000
|
||||
}
|
||||
],
|
||||
next: null
|
||||
}
|
||||
}).as('fetchAllEarningsOnMobile');
|
||||
cy.visit('/affiliate/earnings');
|
||||
cy.wait('@fetchAllEarningsOnMobile');
|
||||
cy.contains('September 2021');
|
||||
cy.contains('Silver - 2');
|
||||
cy.contains('JamTracks Sold: 3');
|
||||
cy.contains('Earnings: $50.00');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import { day } from 'is_js';
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('affiliate signups', () => {
|
||||
beforeEach(() => {
|
||||
const currentUser = makeFakeUser();
|
||||
cy.stubAuthenticate({ id: currentUser.id });
|
||||
//cy.stubGonSubscriptionCodes();
|
||||
});
|
||||
|
||||
describe('when user has no signups', () => {
|
||||
it('should show no signups message', () => {
|
||||
cy.intercept('GET', /\S+\/api\/affiliate_partners\/signups?\S+/, {
|
||||
body: [
|
||||
{
|
||||
traffics: [],
|
||||
next: null
|
||||
}
|
||||
]
|
||||
}).as('fetchAllSignups');
|
||||
cy.visit('/affiliate/signups');
|
||||
cy.wait('@fetchAllSignups');
|
||||
cy.contains('There is no signup data yet ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user has signups', () => {
|
||||
it('should show signups', () => {
|
||||
cy.intercept('GET', /\S+\/api\/affiliate_partners\/signups?\S+/, {
|
||||
body: {
|
||||
traffics: [
|
||||
{
|
||||
day: '2021-09-01',
|
||||
visits: 100,
|
||||
signups: 10,
|
||||
},
|
||||
{
|
||||
day: '2021-09-02',
|
||||
visits: 200,
|
||||
signups: 20,
|
||||
}
|
||||
]
|
||||
}
|
||||
}).as('fetchAllSignups');
|
||||
cy.visit('/affiliate/signups');
|
||||
cy.wait('@fetchAllSignups');
|
||||
cy.contains('Signups');
|
||||
cy.get('[data-testid=affiliateSignupList] tbody tr:first-child > td').eq(0).should('have.text', 'September 2021');
|
||||
cy.get('[data-testid=affiliateSignupList] tbody tr:first-child > td').eq(1).should('have.text', '300');
|
||||
cy.get('[data-testid=affiliateSignupList] tbody tr:first-child > td').eq(2).should('have.text', '30');
|
||||
});
|
||||
});
|
||||
|
||||
describe('in mobile', () => {
|
||||
it('should show signups in mobile', () => {
|
||||
cy.viewport('iphone-6');
|
||||
cy.intercept('GET', /\S+\/api\/affiliate_partners\/signups?\S+/, {
|
||||
body: {
|
||||
traffics: [
|
||||
{
|
||||
day: '2021-09-01',
|
||||
visits: 100,
|
||||
signups: 25,
|
||||
},
|
||||
{
|
||||
day: '2021-10-01',
|
||||
visits: 50,
|
||||
signups: 10,
|
||||
},
|
||||
{
|
||||
day: '2021-10-02',
|
||||
visits: 10,
|
||||
signups: 5,
|
||||
}
|
||||
]
|
||||
}
|
||||
}).as('fetchAllSignups');
|
||||
cy.visit('/affiliate/signups');
|
||||
cy.wait('@fetchAllSignups');
|
||||
cy.contains('Signups');
|
||||
cy.get('[data-testid=affiliateSignupsSwiper] .swiper-slide').should('have.length', 2);
|
||||
cy.get('[data-testid=affiliateSignupsSwiper] .swiper-button-next').click();
|
||||
cy.get('[data-testid=affiliateSignupsSwiper] .swiper-slide').contains('October 2021');
|
||||
cy.get('[data-testid=affiliateSignupsSwiper] .swiper-slide').contains('Visits: 60');
|
||||
cy.get('[data-testid=affiliateSignupsSwiper] .swiper-slide').contains('Signups: 15');
|
||||
});
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
///<reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('forgot password', () => {
|
||||
beforeEach(() => {
|
||||
const currentUser = makeFakeUser({
|
||||
email: 'sam@example.com'
|
||||
});
|
||||
cy.clearCookie('remeber_token');
|
||||
});
|
||||
|
||||
it('redirects to forgot password page', () => {
|
||||
cy.visit('/');
|
||||
cy.url().should('include', '/auth/login');
|
||||
cy.get('a')
|
||||
.contains('Forgot password?')
|
||||
.click();
|
||||
cy.url().should('include', '/auth/forget-password');
|
||||
cy.get('h5').contains('Forgot Your Password');
|
||||
});
|
||||
|
||||
describe('validate forgot password form', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/auth/forget-password');
|
||||
cy.get('[data-testid=email]').clear();
|
||||
cy.get('[data-testid=submit]').should('be.disabled');
|
||||
});
|
||||
|
||||
//invalid email format
|
||||
it('invalid email format', () => {
|
||||
cy.get('[data-testid=email]').type('invalid-email-format@example');
|
||||
cy.get('[data-testid=submit]').click();
|
||||
cy.url().should('not.include', /\/auth\/confirm-mail?\S+/);
|
||||
});
|
||||
|
||||
//valid email format but non-existing
|
||||
it('valid email format but non-existing', () => {
|
||||
cy.get('[data-testid=email]').type('valid-email-format@example.com');
|
||||
cy.get('[data-testid=submit]').click();
|
||||
cy.url().should('not.include', /\/auth\/confirm-mail?\S+/);
|
||||
});
|
||||
|
||||
//valid and existing email
|
||||
it('valid and existing email', () => {
|
||||
cy.get('[data-testid=email]').type('nuwan@jamkazam.com');
|
||||
cy.get('[data-testid=submit]').click();
|
||||
cy.wait(3000);
|
||||
cy.contains('Please check your email!');
|
||||
cy.url().should('match', /\S+auth\/confirm-mail?\S+/);
|
||||
cy.contains('An email has been sent to nuwan@jamkazam.com.')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
///<reference types="cypress" />
|
||||
|
||||
function submitLogin(){
|
||||
const userAttrs = {
|
||||
id: '1',
|
||||
first_name: 'Peter',
|
||||
last_name: 'Pan',
|
||||
name: 'Peter Pan',
|
||||
photo_url: '',
|
||||
email: 'peter@example.com'
|
||||
}
|
||||
|
||||
cy.intercept('POST', `${Cypress.env('apiBaseUrl')}/auths/login`, {
|
||||
statusCode: 200,
|
||||
body: userAttrs
|
||||
}).as('createSession')
|
||||
|
||||
cy.intercept('GET', `${Cypress.env('apiBaseUrl')}/me`, {
|
||||
statusCode: 200,
|
||||
body: userAttrs
|
||||
}).as('getCurrentUser')
|
||||
|
||||
cy.reload()
|
||||
cy.get('[data-testid=email]').type('peter@example.com')
|
||||
cy.get('[data-testid=password]').type('jam123')
|
||||
cy.get('[data-testid=submit]').click()
|
||||
|
||||
}
|
||||
|
||||
describe('Unauthenticated users redirect to login page', () => {
|
||||
it('redirects to login page', () => {
|
||||
cy.clearCookie('remeber_token')
|
||||
cy.visit('/')
|
||||
cy.url().should('include', '/auth/login')
|
||||
cy.contains('Sign In')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Login page', () => {
|
||||
before(() => {
|
||||
cy.clearCookie('remeber_token')
|
||||
})
|
||||
|
||||
it('validate login form', () => {
|
||||
cy.visit('/auth/login')
|
||||
cy.reload()
|
||||
cy.get('[data-testid=submit]').should('be.disabled')
|
||||
cy.get('[data-testid=email]').type('invalid-email-format@example')
|
||||
cy.get('[data-testid=submit]').should('be.disabled')
|
||||
cy.get('[data-testid=email]').type('valid-email-format@example.com')
|
||||
cy.get('[data-testid=submit]').should('be.disabled')
|
||||
cy.get('[data-testid=password]').type('password')
|
||||
cy.get('[data-testid=submit]').should('not.be.disabled')
|
||||
})
|
||||
|
||||
it('submit login form with invalid credentials', () => {
|
||||
cy.visit('/auth/login')
|
||||
cy.reload()
|
||||
cy.get('[data-testid=email]').type('peter@example.com')
|
||||
cy.get('[data-testid=password]').type('wrong')
|
||||
cy.get('[data-testid=submit]').click()
|
||||
cy.contains('Incorrect email or password')
|
||||
})
|
||||
|
||||
it('submits login form', () => {
|
||||
cy.visit('/auth/login')
|
||||
submitLogin()
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/') // tests won't fail in case the port changes
|
||||
//cy.contains('Signed in as peter@example.com')
|
||||
//cy.contains('Peter Pan')
|
||||
//cy.getCookie('remember_token').should('exist')
|
||||
})
|
||||
|
||||
it('redirect to requested page', () => {
|
||||
cy.visit('/friends')
|
||||
cy.url().should('include', '/auth/login')
|
||||
cy.reload()
|
||||
submitLogin()
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/friends')
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('Forget password page', () => {
|
||||
it('submit forget password form', () => {
|
||||
cy.visit('/auth/forget-password')
|
||||
cy.get('[data-testid=email]').type('peter@example.com')
|
||||
cy.get('[data-testid=submit]').click()
|
||||
cy.contains('An email is sent')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,611 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
const showSidePanelContent = () => {
|
||||
cy.get('[data-testid=profileSidePanel] h4').should('have.text', 'Test User1');
|
||||
cy.get('[data-testid=profileSidePanel] .modal-body').first().within(() => {
|
||||
cy.contains('Location: Denver, CO, US')
|
||||
.and('contain', 'Skill Level: Professional')
|
||||
.and('contain', 'Joined JamKazam: 08-26-2021')
|
||||
.and('contain', 'Last Active:')
|
||||
.and('contain', 'Latency To Me:');
|
||||
cy.get('.latency-badge').contains('UNKNOWN');
|
||||
|
||||
cy.get('[data-testid=biography]').contains('Biography of Test User1');
|
||||
|
||||
//instruments
|
||||
cy.get('[data-testid=instruments]').contains('Acoustic Guitar: Expert');
|
||||
cy.get('[data-testid=instruments]').contains('Keyboard: Expert');
|
||||
|
||||
//genres
|
||||
cy.get('[data-testid=genres]').contains('classical, blues');
|
||||
|
||||
//bands
|
||||
cy.get('[data-testid=bands]').contains('The Band');
|
||||
|
||||
//performance_samples
|
||||
cy.get('[data-testid=performance_samples]').contains('Test Recording1')//.should('have.attr', 'href').and('eq', 'https://www.jamkazam.com/test-recording1');
|
||||
|
||||
//online presence
|
||||
cy.get('[data-testid=online_presences]').contains('Youtube').should('have.attr', 'href').and('eq', 'https://www.youtube.com/testuser');
|
||||
cy.get('[data-testid=online_presences]').contains('Facebook').should('have.attr', 'href').and('eq', 'https://www.facebook.com/testuser');
|
||||
cy.get('[data-testid=online_presences]').contains('Twitter').should('have.attr', 'href').and('eq', 'https://www.twitter.com/testuser');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const closeSidePanel = () => {
|
||||
cy.get('[data-testid=profileSidePanel] .modal-header button.close').click();
|
||||
};
|
||||
|
||||
describe('Friends page without data', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.intercept('POST', /\S+\/filter/, {
|
||||
musicians: []
|
||||
}).as("getPeople_empty");
|
||||
});
|
||||
|
||||
//from_location checkbox is hidden for now. so we skip this test for now
|
||||
it.skip('from_location is unchecked', () => {
|
||||
cy.visit('/friends');
|
||||
|
||||
//default api call with from_location parameter turned off
|
||||
cy.wait('@getPeople_empty')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '1st API call');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
from_location: false
|
||||
});
|
||||
|
||||
//now it automatically turns on from_location parameter and fetches again
|
||||
cy.wait('@getPeople_empty')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '2nd API call - (prefetched)');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
from_location: true
|
||||
});
|
||||
|
||||
cy.contains('No Records!');
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=from_location]').check();
|
||||
cy.wait(1000);
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
//default api call with from_location parameter turned on
|
||||
cy.wait('@getPeople_empty')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '3th API call');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
from_location: true
|
||||
});
|
||||
|
||||
cy.contains('No Records!');
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('Friends page with data', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: '2' }); //currentUser id is 2 - people.yaml fixture
|
||||
cy.intercept('POST', /\S+\/filter\?offset=0/, { fixture: 'people_page1' }).as('getPeople_page1');
|
||||
cy.intercept('POST', /\S+\/filter\?offset=10/, { fixture: 'people_page2' }).as('getPeople_page2');
|
||||
cy.intercept('POST', /\S+\/filter\?offset=20/, { fixture: 'people_page3' }).as('getPeople_page3');
|
||||
cy.intercept('GET', /\S+\/profile/, { fixture: 'person' });
|
||||
cy.intercept('GET', /\S+\/my_notifications\S+/, {
|
||||
statusCode: 200,
|
||||
body: []
|
||||
});
|
||||
});
|
||||
|
||||
describe('listing users', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
describe('in desktop', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('macbook-13');
|
||||
});
|
||||
|
||||
it('paginate', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 10);
|
||||
cy.wait('@getPeople_page2')
|
||||
cy.get('[data-testid=paginate-next-page]').click();
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 20);
|
||||
cy.get('[data-testid=paginate-next-page]').click();
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 30);
|
||||
cy.get('[data-testid=paginate-next-page]').should('not.exist');
|
||||
});
|
||||
|
||||
it('show profiles', () => {
|
||||
cy.contains('Find New Friends').should('exist');
|
||||
cy.contains('Update Search').should('exist');
|
||||
cy.contains('Reset Filters').should('exist');
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.should('have.length', 10)
|
||||
.first()
|
||||
.contains('Test User1');
|
||||
});
|
||||
|
||||
it('click profile name', () => {
|
||||
//open side panel by clicking name
|
||||
cy.get('[data-testid=peopleListTable]').find('.person-link').first().within(() => {
|
||||
cy.contains("Test User1").click()
|
||||
})
|
||||
//cy.get('[data-testid=peopleListTable]').find('.person-link').first().click()
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click more button', () => {
|
||||
//open side panel by clicking more button
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=btnMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click more link', () => {
|
||||
//open side panel by clicking more link
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=linkMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click description more link', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('td[data-testid=biography-col]')
|
||||
.within(() => {
|
||||
cy.contains('More').click();
|
||||
});
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click instruments more link', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=instrumentList]')
|
||||
.within(() => {
|
||||
cy.get('[data-testid=instrument]').should('have.length', 4); //show only 4 instruments plus more link
|
||||
cy.contains('Acoustic Guitar: Expert');
|
||||
cy.contains('Keyboard: Expert');
|
||||
cy.contains('Ukulele: Expert');
|
||||
cy.contains('Voice: Expert');
|
||||
cy.contains('More').click();
|
||||
});
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click genres more link', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('td[data-testid=genres-col]')
|
||||
.within(() => {
|
||||
cy.contains('More').click();
|
||||
});
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('in mobile', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('iphone-6');
|
||||
});
|
||||
|
||||
it('show profile', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide').should('have.length', 10);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.eq(0)
|
||||
.contains('Test User1');
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.eq(0)
|
||||
.should('be.visible');
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.eq(2)
|
||||
.should('not.be.visible');
|
||||
});
|
||||
|
||||
it('show all profile description', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=mobBiography]')
|
||||
.should('not.contain', 'More');
|
||||
});
|
||||
|
||||
it('show all instruments', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=instrumentList] div')
|
||||
.its('length')
|
||||
.should('be.gte', 1);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=instrumentList]')
|
||||
.should('not.contain', 'More');
|
||||
});
|
||||
|
||||
it('show all genres', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=genreList] div')
|
||||
.its('length')
|
||||
.should('be.gte', 1);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=genreList]')
|
||||
.should('not.contain', 'More');
|
||||
});
|
||||
|
||||
//it.skip('click connect button', () => {});
|
||||
|
||||
//it.skip('click message button', () => {});
|
||||
|
||||
it.skip('paginate', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-button-prev').should('have.class', 'swiper-button-disabled');
|
||||
for (let i = 0; i < 19; i++) {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-button-next').click();
|
||||
cy.wait(500);
|
||||
}
|
||||
cy.wait(500);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-button-next').should('have.class', 'swiper-button-disabled');
|
||||
});
|
||||
|
||||
it('click more button', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=btnMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('making friendship', () => {
|
||||
it('add friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User2').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
// cy.get('[data-testid=confirmFriendRequestModal]').within(() => {
|
||||
// cy.contains('Send a friend request to Test User2');
|
||||
// cy.contains('Yes').click();
|
||||
// });
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.disabled');
|
||||
cy.contains('Success! Your friend request has been sent to Test User2.');
|
||||
});
|
||||
|
||||
it('remove friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile/, { fixture: 'friend' });
|
||||
cy.intercept('DELETE', /\S+\/friends\S+/, { statusCode: 204, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User1').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('exist')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.exist')
|
||||
.should('not.be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('chat window', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('is not disabled for friends', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.should('not.be.disabled')
|
||||
.trigger('mouseover');
|
||||
cy.contains('Send a message').should('exist');
|
||||
});
|
||||
|
||||
it('is not disabled for non friends', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(1)
|
||||
.find('[data-testid=message]')
|
||||
.should('not.be.disabled');
|
||||
//cy.contains('You can message this user once you are friends').should('exist')
|
||||
});
|
||||
|
||||
it('lists text messages', () => {
|
||||
//initially show the most recent messages on openning chat window modal
|
||||
let numberOfMessages = 10;
|
||||
cy.fixture('text_messages_page1').then(json => {
|
||||
cy.intercept('GET', /\S+\/text_messages\S+/, json).as('getTextMessages');
|
||||
});
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.wait('@getTextMessages');
|
||||
cy.get('[data-testid=textMessageModal]')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
cy.get('.text-message-row').should('have.length', numberOfMessages);
|
||||
|
||||
//display previous messages by scrolling up
|
||||
const messageFixtures = ['text_messages_page2', 'text_messages_page3'];
|
||||
messageFixtures.forEach(fixture => {
|
||||
cy.fixture(fixture).then(json => {
|
||||
cy.intercept('GET', /\S+\/text_messages\S+/, json);
|
||||
cy.get('.modal-body .ScrollbarsCustom-Scroller')
|
||||
.trigger('mouseover')
|
||||
.scrollTo('bottom');
|
||||
cy.get('.modal-body .ScrollbarsCustom-Scroller')
|
||||
.trigger('mouseover')
|
||||
.scrollTo('top');
|
||||
numberOfMessages = numberOfMessages + 10;
|
||||
cy.get('.text-message-row').should('have.length', numberOfMessages);
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('button')
|
||||
.contains('Close')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
});
|
||||
cy.get('[data-testid=textMessageModal]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('sends message by clicking send button', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
cy.get('textarea').type('Hello');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
cy.get('textarea').should('have.value', '');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('sends message by pressing enter key', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
cy.get('textarea').type('Hello{enter}');
|
||||
cy.get('textarea').should('have.value', '');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('goes away by clicking close button', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Close')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
});
|
||||
cy.get('[data-testid=textMessageModal]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it.skip('shows received message by other user', () => {
|
||||
//TODO: this should be test in e2e test
|
||||
});
|
||||
});
|
||||
|
||||
describe('coming from email links', () => {
|
||||
it("opens details sidebar", () => {
|
||||
cy.visit('/friends?open=details&id=1');
|
||||
showSidePanelContent();
|
||||
});
|
||||
|
||||
it("opens chat window", () => {
|
||||
cy.visit('/friends?open=message&id=1');
|
||||
cy.get('[data-testid=textMessageModal]')
|
||||
.should('be.visible')
|
||||
cy.contains('Send Message to Test User1').should('exist');
|
||||
});
|
||||
|
||||
it("sends friend request", () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } });
|
||||
cy.visit('/friends?open=connect&id=1');
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.disabled');
|
||||
cy.contains('Success! Your friend request has been sent to Test User1.');
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('filter', () => {
|
||||
const fillFilterForm = () => {
|
||||
//cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_fair]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_high]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_beginner]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_intermediate]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_expert]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_expert]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=from_location]').uncheck();
|
||||
cy.get('#selInstruments')
|
||||
.type('Drums{enter}')
|
||||
.click();
|
||||
cy.get('#selGenres')
|
||||
.type('Pop{enter}')
|
||||
.click();
|
||||
cy.get('#selLastActive').type('Within last 1 Day{enter}');
|
||||
cy.get('#selJoinedWithin').type('Within last 7 Day{enter}');
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('open and close filter modal', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').should('be.visible');
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('render filter form', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').within(() => {
|
||||
cy.get('input[name=latency_good]')
|
||||
.should('be.checked')
|
||||
.next()
|
||||
.contains('Good (less than 40ms)');
|
||||
cy.get('input[name=latency_fair]')
|
||||
.should('be.checked')
|
||||
.next()
|
||||
.contains('Fair (40-60ms)');
|
||||
cy.get('input[name=latency_high]')
|
||||
.should('not.be.checked')
|
||||
.next()
|
||||
.contains('Poor (more than 60ms)');
|
||||
cy.get('input[name=from_location]')
|
||||
.should('not.be.checked')
|
||||
});
|
||||
});
|
||||
|
||||
it('reset filters', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
fillFilterForm();
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').should('not.be.checked');
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=btnResetSearch]').click(); //click reset button
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').should('be.checked');
|
||||
});
|
||||
|
||||
it('submit filter form with params', () => {
|
||||
//wait for stubbed request sent to fetch data for initial page load
|
||||
cy.wait('@getPeople_page1').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '1st API call has data');
|
||||
});
|
||||
//the subsequent request sent to perfetch data and store in redux prefetched buffer
|
||||
cy.wait('@getPeople_page2').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '1st API call has data - (prefethed)');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.wait(1000);
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
//wait for stubbed request sent by submitting search form without filling any form field
|
||||
cy.wait('@getPeople_page1')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '3rd API call has data');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
latency_good: true,
|
||||
latency_fair: true,
|
||||
latency_high: false,
|
||||
proficiency_beginner: true,
|
||||
proficiency_intermediate: true,
|
||||
proficiency_expert: true,
|
||||
instruments: [],
|
||||
genres: [],
|
||||
from_location: false
|
||||
});
|
||||
|
||||
cy.wait('@getPeople_page2').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '4th API call has data - (prefethed)');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
fillFilterForm(); // change filter options
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
//wait for stubbed request sent by submitting search form again. but this time fill form fields
|
||||
cy.wait('@getPeople_page1')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '5th API call has data');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
latency_good: false,
|
||||
latency_fair: false,
|
||||
latency_high: false,
|
||||
proficiency_beginner: false,
|
||||
proficiency_intermediate: false,
|
||||
proficiency_expert: false,
|
||||
instruments: [{ value: 'drums', label: 'Drums' }],
|
||||
genres: ['pop'],
|
||||
from_location: false
|
||||
});
|
||||
|
||||
cy.wait('@getPeople_page2').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '6th API call has data - (prefethed)');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
const showSidePanelContent = () => {
|
||||
cy.get('[data-testid=profileSidePanel] h4').should('have.text', 'Test User1');
|
||||
cy.get('[data-testid=profileSidePanel] .modal-body').first().within(() => {
|
||||
cy.contains('Location: Denver, CO, US')
|
||||
.and('contain', 'Skill Level: Professional')
|
||||
.and('contain', 'Joined JamKazam: 08-26-2021')
|
||||
.and('contain', 'Last Active:')
|
||||
.and('contain', 'Latency To Me:');
|
||||
cy.get('.latency-badge').contains('UNKNOWN');
|
||||
|
||||
cy.get('[data-testid=biography]').contains('Biography of Test User1');
|
||||
|
||||
//instruments
|
||||
cy.get('[data-testid=instruments]').contains('Acoustic Guitar: Expert');
|
||||
cy.get('[data-testid=instruments]').contains('Keyboard: Expert');
|
||||
|
||||
//genres
|
||||
cy.get('[data-testid=genres]').contains('classical, blues');
|
||||
|
||||
//bands
|
||||
cy.get('[data-testid=bands]').contains('The Band');
|
||||
|
||||
//performance_samples
|
||||
cy.get('[data-testid=performance_samples]').contains('Test Recording1')//.should('have.attr', 'href').and('eq', 'https://www.jamkazam.com/test-recording1');
|
||||
|
||||
//online presence
|
||||
cy.get('[data-testid=online_presences]').contains('Youtube').should('have.attr', 'href').and('eq', 'https://www.youtube.com/testuser');
|
||||
cy.get('[data-testid=online_presences]').contains('Facebook').should('have.attr', 'href').and('eq', 'https://www.facebook.com/testuser');
|
||||
cy.get('[data-testid=online_presences]').contains('Twitter').should('have.attr', 'href').and('eq', 'https://www.twitter.com/testuser');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const closeSidePanel = () => {
|
||||
cy.get('[data-testid=profileSidePanel] .modal-header button.close').click();
|
||||
};
|
||||
|
||||
describe('Friends page without data', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
// cy.intercept('POST', /\S+\/filter/, {
|
||||
// musicians: []
|
||||
// }).as("getPeople_empty");
|
||||
});
|
||||
|
||||
it('shows no records found alert', () => {
|
||||
cy.visit('/friends/my');
|
||||
|
||||
//default api call with from_location parameter turned off
|
||||
// cy.wait('@getPeople_empty')
|
||||
// .then(interception => {
|
||||
// assert.isNotNull(interception.response.body, '1st API call');
|
||||
// })
|
||||
// .its('request.body')
|
||||
// .should('deep.contain', {
|
||||
// from_location: false
|
||||
// });
|
||||
|
||||
// //now it automatically turns on from_location parameter and fetches again
|
||||
// cy.wait('@getPeople_empty')
|
||||
// .then(interception => {
|
||||
// assert.isNotNull(interception.response.body, '2nd API call - (prefetched)');
|
||||
// })
|
||||
// .its('request.body')
|
||||
// .should('deep.contain', {
|
||||
// from_location: true
|
||||
// });
|
||||
|
||||
// cy.contains('No Records!');
|
||||
|
||||
// cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
// cy.get('[data-testid=modalUpdateSearch] input[name=from_location]').check();
|
||||
// cy.wait(1000);
|
||||
// cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
// //default api call with from_location parameter turned on
|
||||
// cy.wait('@getPeople_empty')
|
||||
// .then(interception => {
|
||||
// assert.isNotNull(interception.response.body, '3th API call');
|
||||
// })
|
||||
// .its('request.body')
|
||||
// .should('deep.contain', {
|
||||
// from_location: true
|
||||
// });
|
||||
|
||||
// cy.contains('No Records!');
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('Friends page with data', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: '2' }); //currentUser id is 2 - people.yaml fixture
|
||||
cy.intercept('POST', /\S+\/filter\?offset=0/, { fixture: 'people_page1' }).as('getPeople_page1');
|
||||
cy.intercept('POST', /\S+\/filter\?offset=10/, { fixture: 'people_page2' }).as('getPeople_page2');
|
||||
cy.intercept('POST', /\S+\/filter\?offset=20/, { fixture: 'people_page3' }).as('getPeople_page3');
|
||||
cy.intercept('GET', /\S+\/profile/, { fixture: 'person' });
|
||||
});
|
||||
|
||||
describe('listing users', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
describe('in desktop', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('macbook-13');
|
||||
});
|
||||
|
||||
it('paginate', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 10);
|
||||
cy.wait('@getPeople_page2')
|
||||
cy.get('[data-testid=paginate-next-page]').click();
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 20);
|
||||
//cy.get('[data-testid=paginate-next-page]').should('not.exist');
|
||||
cy.get('[data-testid=paginate-next-page]').click();
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 30);
|
||||
cy.get('[data-testid=paginate-next-page]').should('not.exist');
|
||||
});
|
||||
|
||||
it('show profiles', () => {
|
||||
cy.contains('Find New Friends').should('exist');
|
||||
cy.contains('Update Search').should('exist');
|
||||
cy.contains('Reset Filters').should('exist');
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.should('have.length', 10)
|
||||
.first()
|
||||
.contains('Test User1');
|
||||
});
|
||||
|
||||
it('click profile name', () => {
|
||||
//open side panel by clicking name
|
||||
cy.get('[data-testid=peopleListTable]').find('.person-link').first().within(() => {
|
||||
cy.contains("Test User1").click()
|
||||
})
|
||||
//cy.get('[data-testid=peopleListTable]').find('.person-link').first().click()
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click more button', () => {
|
||||
//open side panel by clicking more button
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=btnMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click more link', () => {
|
||||
//open side panel by clicking more link
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=linkMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click description more link', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('td[data-testid=biography-col]')
|
||||
.within(() => {
|
||||
cy.contains('More').click();
|
||||
});
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click instruments more link', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=instrumentList]')
|
||||
.within(() => {
|
||||
cy.get('[data-testid=instrument]').should('have.length', 4); //show only 4 instruments plus more link
|
||||
cy.contains('Acoustic Guitar: Expert');
|
||||
cy.contains('Keyboard: Expert');
|
||||
cy.contains('Ukulele: Expert');
|
||||
cy.contains('Voice: Expert');
|
||||
cy.contains('More').click();
|
||||
});
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
it('click genres more link', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('td[data-testid=genres-col]')
|
||||
.within(() => {
|
||||
cy.contains('More').click();
|
||||
});
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('in mobile', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('iphone-6');
|
||||
});
|
||||
|
||||
it('show profile', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide').should('have.length', 10);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.eq(0)
|
||||
.contains('Test User1');
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.eq(0)
|
||||
.should('be.visible');
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.eq(2)
|
||||
.should('not.be.visible');
|
||||
});
|
||||
|
||||
it('show all profile description', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=mobBiography]')
|
||||
.should('not.contain', 'More');
|
||||
});
|
||||
|
||||
it('show all instruments', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=instrumentList] div')
|
||||
.its('length')
|
||||
.should('be.gte', 1);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=instrumentList]')
|
||||
.should('not.contain', 'More');
|
||||
});
|
||||
|
||||
it('show all genres', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=genreList] div')
|
||||
.its('length')
|
||||
.should('be.gte', 1);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=genreList]')
|
||||
.should('not.contain', 'More');
|
||||
});
|
||||
|
||||
//it.skip('click connect button', () => {});
|
||||
|
||||
//it.skip('click message button', () => {});
|
||||
|
||||
it.skip('paginate', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-button-prev').should('have.class', 'swiper-button-disabled');
|
||||
for (let i = 0; i < 19; i++) {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-button-next').click();
|
||||
cy.wait(500);
|
||||
}
|
||||
cy.wait(500);
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-button-next').should('have.class', 'swiper-button-disabled');
|
||||
});
|
||||
|
||||
it('click more button', () => {
|
||||
cy.get('[data-testid=peopleSwiper] .swiper-slide')
|
||||
.first()
|
||||
.find('[data-testid=btnMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('making friendship', () => {
|
||||
it('add friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User2').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
// cy.get('[data-testid=confirmFriendRequestModal]').within(() => {
|
||||
// cy.contains('Send a friend request to Test User2');
|
||||
// cy.contains('Yes').click();
|
||||
// });
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.disabled');
|
||||
cy.contains('Success! Your friend request has been sent to Test User2.');
|
||||
});
|
||||
|
||||
it('remove friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile/, { fixture: 'friend' });
|
||||
cy.intercept('DELETE', /\S+\/friends\S+/, { statusCode: 204, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User1').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('exist')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.exist')
|
||||
.should('not.be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('chat window', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('is not disabled for friends', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.should('not.be.disabled')
|
||||
.trigger('mouseover');
|
||||
cy.contains('Send a message').should('exist');
|
||||
});
|
||||
|
||||
it('is not disabled for non friends', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(1)
|
||||
.find('[data-testid=message]')
|
||||
.should('not.be.disabled');
|
||||
//cy.contains('You can message this user once you are friends').should('exist')
|
||||
});
|
||||
|
||||
it('lists text messages', () => {
|
||||
//initially show the most recent messages on openning chat window modal
|
||||
let numberOfMessages = 10;
|
||||
cy.fixture('text_messages_page1').then(json => {
|
||||
cy.intercept('GET', /\S+\/text_messages\S+/, json).as('getTextMessages');
|
||||
});
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.wait('@getTextMessages');
|
||||
cy.get('[data-testid=textMessageModal]')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
cy.get('.text-message-row').should('have.length', numberOfMessages);
|
||||
|
||||
//display previous messages by scrolling up
|
||||
const messageFixtures = ['text_messages_page2', 'text_messages_page3'];
|
||||
messageFixtures.forEach(fixture => {
|
||||
cy.fixture(fixture).then(json => {
|
||||
cy.intercept('GET', /\S+\/text_messages\S+/, json);
|
||||
cy.get('.modal-body .ScrollbarsCustom-Scroller')
|
||||
.trigger('mouseover')
|
||||
.scrollTo('bottom');
|
||||
cy.get('.modal-body .ScrollbarsCustom-Scroller')
|
||||
.trigger('mouseover')
|
||||
.scrollTo('top');
|
||||
numberOfMessages = numberOfMessages + 10;
|
||||
cy.get('.text-message-row').should('have.length', numberOfMessages);
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('button')
|
||||
.contains('Close')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
});
|
||||
cy.get('[data-testid=textMessageModal]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('sends message by clicking send button', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
cy.get('textarea').type('Hello');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
cy.get('textarea').should('have.value', '');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('sends message by pressing enter key', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
cy.get('textarea').type('Hello{enter}');
|
||||
cy.get('textarea').should('have.value', '');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('goes away by clicking close button', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Close')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
});
|
||||
cy.get('[data-testid=textMessageModal]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it.skip('shows received message by other user', () => {
|
||||
//TODO: this should be test in e2e test
|
||||
});
|
||||
});
|
||||
|
||||
describe('coming from email links', () => {
|
||||
it("opens details sidebar", () => {
|
||||
cy.visit('/friends?open=details&id=1');
|
||||
showSidePanelContent();
|
||||
});
|
||||
|
||||
it("opens chat window", () => {
|
||||
cy.visit('/friends?open=message&id=1');
|
||||
cy.get('[data-testid=textMessageModal]')
|
||||
.should('be.visible')
|
||||
cy.contains('Send Message to Test User1').should('exist');
|
||||
});
|
||||
|
||||
it("sends friend request", () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } });
|
||||
cy.visit('/friends?open=connect&id=1');
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.disabled');
|
||||
cy.contains('Success! Your friend request has been sent to Test User1.');
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('filter', () => {
|
||||
const fillFilterForm = () => {
|
||||
//cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_fair]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_high]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_beginner]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_intermediate]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_expert]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_expert]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=from_location]').uncheck();
|
||||
cy.get('#selInstruments')
|
||||
.type('Drums{enter}')
|
||||
.click();
|
||||
cy.get('#selGenres')
|
||||
.type('Pop{enter}')
|
||||
.click();
|
||||
cy.get('#selLastActive').type('Within last 1 Day{enter}');
|
||||
cy.get('#selJoinedWithin').type('Within last 7 Day{enter}');
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('open and close filter modal', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').should('be.visible');
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('render filter form', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').within(() => {
|
||||
cy.get('input[name=latency_good]')
|
||||
.should('be.checked')
|
||||
.next()
|
||||
.contains('Good (less than 40ms)');
|
||||
cy.get('input[name=latency_fair]')
|
||||
.should('be.checked')
|
||||
.next()
|
||||
.contains('Fair (40-60ms)');
|
||||
cy.get('input[name=latency_high]')
|
||||
.should('not.be.checked')
|
||||
.next()
|
||||
.contains('Poor (more than 60ms)');
|
||||
cy.get('input[name=from_location]')
|
||||
.should('not.be.checked')
|
||||
});
|
||||
});
|
||||
|
||||
it('reset filters', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
fillFilterForm();
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').should('not.be.checked');
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=btnResetSearch]').click(); //click reset button
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').should('be.checked');
|
||||
});
|
||||
|
||||
it('submit filter form with params', () => {
|
||||
//wait for stubbed request sent to fetch data for initial page load
|
||||
cy.wait('@getPeople_page1').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '1st API call has data');
|
||||
});
|
||||
//the subsequent request sent to perfetch data and store in redux prefetched buffer
|
||||
cy.wait('@getPeople_page2').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '1st API call has data - (prefethed)');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.wait(1000);
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
//wait for stubbed request sent by submitting search form without filling any form field
|
||||
cy.wait('@getPeople_page1')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '3rd API call has data');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
latency_good: true,
|
||||
latency_fair: true,
|
||||
latency_high: false,
|
||||
proficiency_beginner: true,
|
||||
proficiency_intermediate: true,
|
||||
proficiency_expert: true,
|
||||
instruments: [],
|
||||
genres: [],
|
||||
from_location: false
|
||||
});
|
||||
|
||||
cy.wait('@getPeople_page2').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '4th API call has data - (prefethed)');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
fillFilterForm(); // change filter options
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
//wait for stubbed request sent by submitting search form again. but this time fill form fields
|
||||
cy.wait('@getPeople_page1')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '5th API call has data');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.contain', {
|
||||
latency_good: false,
|
||||
latency_fair: false,
|
||||
latency_high: false,
|
||||
proficiency_beginner: false,
|
||||
proficiency_intermediate: false,
|
||||
proficiency_expert: false,
|
||||
instruments: [{ value: 'drums', label: 'Drums' }],
|
||||
genres: ['pop'],
|
||||
from_location: false
|
||||
});
|
||||
|
||||
cy.wait('@getPeople_page2').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '6th API call has data - (prefethed)');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Unsubscribe from email link', () => {
|
||||
beforeEach(() => {
|
||||
// cy.intercept('POST', /\S+\/unsubscribe_user_match\/\S+/, { statusCode: 200, body: { ok: true } });
|
||||
const currentUser = makeFakeUser({
|
||||
email: 'sam@example.com'
|
||||
});
|
||||
cy.stubAuthenticate({ ...currentUser });
|
||||
cy.intercept('POST', /\S+\/unsubscribe\/\S+/, { statusCode: 200, body: { ok: true } });
|
||||
})
|
||||
|
||||
it("redirect to home page if tok is not provided", () => {
|
||||
cy.visit('/unsubscribe');
|
||||
cy.location('pathname').should('eq', '/errors/404');
|
||||
});
|
||||
|
||||
it("show unsubscribed message", () => {
|
||||
cy.visit('/unsubscribe/123');
|
||||
// cy.location('search')
|
||||
// .should('equal', '?tok=123')
|
||||
// .then((s) => new URLSearchParams(s))
|
||||
// .invoke('get', 'tok')
|
||||
// .should('equal', '123')
|
||||
cy.contains("You have successfully unsubscribed from JamKazam emails.").should('be.visible');
|
||||
cy.contains("Loading...").should('not.exist');
|
||||
});
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('JamTracks Page', () => {
|
||||
beforeEach(() => {
|
||||
const currentUser = makeFakeUser();
|
||||
cy.stubAuthenticate({ id: currentUser.id });
|
||||
cy.visit('/jamtracks');
|
||||
});
|
||||
|
||||
it('should display the JamTracks page', () => {
|
||||
cy.get('.card-header h5').should('contain', 'Find JamTracks');
|
||||
});
|
||||
|
||||
it('should display the download link', () => {
|
||||
cy.get('[data-testid=download-pdf]')
|
||||
.invoke('attr', 'href')
|
||||
.should('contain', 'https://s3.amazonaws.com/jamkazam-public/public/lists/all-jamkazam-jamtracks.pdf');
|
||||
});
|
||||
|
||||
it('should display the JamTracks search bar', () => {
|
||||
cy.get('input[type="search"]').should('exist');
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
beforeEach(() => {
|
||||
//http://www.jamkazam.local:3000/api/jamtracks/autocomplete?match=ac+&limit=5
|
||||
// cy.intercept('GET', /S+\/jamtracks\/autocomplete\?match=ac\S+/, {
|
||||
// body: [{ artists: [{ original_artist: 'AC DC' }], songs: [] }]
|
||||
// }).as('getJamTracksAutoComplete');
|
||||
cy.intercept('GET', /\S+\/jamtracks\?per_page=10\&page=1\&\S+/, { fixture: 'jamtracks_page1' }).as('getJamTracks_page1');
|
||||
cy.intercept('GET', /\S+\/jamtracks\?per_page=10\&page=2\&\S+/, { fixture: 'jamtracks_page2' }).as('getJamTracks_page2');
|
||||
cy.intercept('GET', /\S+\/jamtracks\?per_page=10\&page=3\&\S+/, { fixture: 'jamtracks_page3' }).as('getJamTracks_page3');
|
||||
cy.intercept('GET', /\S+\/shopping_carts/, {
|
||||
statusCode: 200,
|
||||
body: []
|
||||
}).as('getShoppingCart');
|
||||
cy.intercept('POST', /\S+\/shopping_carts\/add_jamtrack/, {
|
||||
statusCode: 200,
|
||||
body: { id: 1,
|
||||
product_info: {
|
||||
total_price: 9.99
|
||||
}
|
||||
}
|
||||
}).as('addJamTrackToCart');
|
||||
cy.intercept('GET', /\S+\/jamtracks\/purchased/, {
|
||||
statusCode: 200,
|
||||
body: []
|
||||
}).as('getPurchasedJamTracks');
|
||||
})
|
||||
|
||||
it('should display the JamTracks', () => {
|
||||
cy.get('input[type="search"]').type('ba{enter}');
|
||||
cy.wait('@getJamTracks_page1');
|
||||
cy.contains('Search Results: JamTracks including "ba"');
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr:first .track-name-col').should('contain', 'Back in Black by AC DC');
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr:first .track-tracks-col .jamtrack-track:visible').should('have.length', 6);
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr:first .track-tracks-col').contains('Show all tracks').click();
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr:first .track-tracks-col .jamtrack-track:visible').should('have.length', 10);
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr:first .track-tracks-col').contains('Show fewer tracks').click();
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr:first .track-tracks-col .jamtrack-track:visible').should('have.length', 6);
|
||||
|
||||
//load more
|
||||
cy.get('button').contains('Load More').click();
|
||||
cy.wait('@getJamTracks_page2');
|
||||
|
||||
//load more
|
||||
cy.get('button').contains('Load More').click();
|
||||
cy.wait('@getJamTracks_page3');
|
||||
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr').should('have.length', 30);
|
||||
|
||||
//no more pages
|
||||
cy.get('[data-testid=moreBtn]').should('not.exist');
|
||||
});
|
||||
|
||||
it('let user to purchase a JamTrack', () => {
|
||||
cy.get('input[type="search"]').type('ba{enter}');
|
||||
cy.wait('@getJamTracks_page1');
|
||||
cy.wait('@getShoppingCart');
|
||||
cy.get('[data-testid=jamtracks-table] tbody tr').eq(2).find('.purchase-button-col button').should('contain', 'Add to Cart').click();
|
||||
cy.wait('@getShoppingCart');
|
||||
cy.wait('@addJamTrackToCart');
|
||||
cy.location('pathname').should('eq', '/shopping-cart')
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe.only('mobile', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('iphone-6');
|
||||
});
|
||||
|
||||
it('should display the JamTracks', () => {
|
||||
cy.get('input[type="search"]').type('ba{enter}');
|
||||
cy.wait('@getJamTracks_page1');
|
||||
cy.contains('Search Results: JamTracks including "ba"');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('JamTracks Page', () => {
|
||||
beforeEach(() => {
|
||||
const currentUser = makeFakeUser();
|
||||
cy.stubAuthenticate({ id: currentUser.id });
|
||||
cy.visit('/my-jamtracks');
|
||||
});
|
||||
|
||||
it('should display the My JamTracks page', () => {
|
||||
cy.get('.card-header h5').should('contain', 'My JamTracks');
|
||||
});
|
||||
|
||||
it('should display the search bar', () => {
|
||||
cy.get('input[type="search"]').should('exist');
|
||||
});
|
||||
|
||||
describe('filter', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', /\S+\/jamtracks\/purchased\?page=1\&\S+/, { fixture: 'my_jamtracks_page1' }).as('getMyJamTracks_page1');
|
||||
});
|
||||
|
||||
it('should display the JamTracks', () => {
|
||||
cy.get('input[type="search"]').type('ba');
|
||||
cy.wait('@getMyJamTracks_page1');
|
||||
cy.get('[data-testid=myJamTrackList]').should('contain', 'Back in Black by AC DC');
|
||||
});
|
||||
|
||||
it('clicking on a JamTrack should navigate to the JamTrack page', () => {
|
||||
cy.get('input[type="search"]').type('ba');
|
||||
cy.wait('@getMyJamTracks_page1');
|
||||
cy.get('[data-testid=myJamTrackList] a').first().click();
|
||||
cy.url().should('include', '/jamtrack/1');
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
describe('Top Navigation', () => {
|
||||
const showSubscribeToUpdates = () => {
|
||||
cy.contains('Keep JamKazam Improving').should('exist');
|
||||
cy.contains('Subscribe').should('exist');
|
||||
};
|
||||
|
||||
const showProfileDropdown = () => {
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('exist');
|
||||
cy.contains('Sign Out').should('exist');
|
||||
};
|
||||
|
||||
describe('when user has not logged in', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubUnauthenticate();
|
||||
});
|
||||
|
||||
it('shows homepage', () => {
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
cy.contains('Sign in').should('exist');
|
||||
});
|
||||
|
||||
it('not allowed to protected page', () => {
|
||||
cy.visit('/friends');
|
||||
cy.wait('@getAppFeatures');
|
||||
cy.url().should('include', '/auth/login');
|
||||
cy.contains('Sign in');
|
||||
cy.get('button').should('have.text', 'Sign in');
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user has logged in', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
});
|
||||
|
||||
it('shows user dropdown', () => {
|
||||
showSubscribeToUpdates();
|
||||
showProfileDropdown();
|
||||
});
|
||||
|
||||
it('sign out', () => {
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]')
|
||||
.click();
|
||||
cy.stubUnauthenticate();
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]')
|
||||
.contains('Sign Out')
|
||||
.click();
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('not.exist');
|
||||
cy.contains('Sign in');
|
||||
});
|
||||
});
|
||||
|
||||
describe('header notifications', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.intercept('GET', /\S+\/my_notifications/, { fixture: 'notifications' });
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
});
|
||||
|
||||
it('shows notifications', () => {
|
||||
cy.get('[data-testid=notificationDropdown]').should('not.be.visible');
|
||||
cy.get('.bell-icon').click();
|
||||
|
||||
cy.get('[data-testid=notificationDropdown]').should('be.visible');
|
||||
cy.get('[data-testid=notificationDropdown] .list-group-item').should('have.length', 3);
|
||||
cy.get('[data-testid=notificationDropdown]')
|
||||
.contains('View all')
|
||||
.click(); //view all notifications
|
||||
cy.url().should('include', '/notifications');
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('locale switch', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
});
|
||||
|
||||
it('translate', () => {
|
||||
cy.get('.card-header').contains('Home');
|
||||
cy.get('[data-testid=langSwitch]')
|
||||
.contains('ES')
|
||||
.click();
|
||||
cy.get('.card-header').contains('Página de inicio');
|
||||
cy.get('.card-header').should('not.contain', 'Home');
|
||||
cy.get('[data-testid=langSwitch]')
|
||||
.contains('EN')
|
||||
.click();
|
||||
cy.get('.card-header').contains('Home');
|
||||
cy.get('.card-header').should('not.contain', 'Página de inicio');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Side Navigation', () => {
|
||||
describe('backend returns empty set of app features', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.viewport('macbook-13');
|
||||
});
|
||||
|
||||
it('shows all menu items', () => {
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Home');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Sessions');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Create Session');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Friends');
|
||||
});
|
||||
|
||||
it('toggles only one menu on click', () => {
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
cy.get('[data-testid=verticalNavigation]')
|
||||
.contains('Sessions')
|
||||
.click();
|
||||
cy.get('[data-testid=verticalNavigation]')
|
||||
.contains('Create Session')
|
||||
.should('not.visible');
|
||||
cy.get('[data-testid=verticalNavigation]')
|
||||
.contains('Browse Sessions')
|
||||
.should('not.visible');
|
||||
cy.get('[data-testid=verticalNavigation]')
|
||||
.contains('Session History')
|
||||
.should('not.visible');
|
||||
//the Friends menu is not toggled
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Friends');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('My Friends');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('backend returns app features', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.viewport('macbook-13');
|
||||
cy.intercept('GET', /\S+\/app_features/, {
|
||||
statusCode: 200,
|
||||
body: [
|
||||
{
|
||||
handle: '/sessions',
|
||||
is_enabled: true,
|
||||
feature_type: 'page',
|
||||
env: 'development'
|
||||
},
|
||||
{
|
||||
handle: '/sessions/new',
|
||||
is_enabled: false,
|
||||
feature_type: 'page',
|
||||
env: 'development'
|
||||
},
|
||||
{
|
||||
handle: '/sessions/history',
|
||||
is_enabled: true,
|
||||
feature_type: 'page',
|
||||
env: 'production'
|
||||
},
|
||||
{
|
||||
handle: '/friends',
|
||||
is_enabled: true,
|
||||
feature_type: 'page',
|
||||
env: 'development'
|
||||
},
|
||||
{
|
||||
handle: '/friends/my',
|
||||
is_enabled: true,
|
||||
feature_type: 'page',
|
||||
env: 'development'
|
||||
}
|
||||
]
|
||||
}).as('getAppFeatures');
|
||||
});
|
||||
|
||||
it('shows only enabled menu items', () => {
|
||||
cy.visit('/');
|
||||
cy.wait('@getAppFeatures');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Friends');
|
||||
cy.get('[data-testid=verticalNavigation]').contains('Sessions');
|
||||
cy.get('[data-testid=verticalNavigation]').should('not.include.text', 'Create Session')
|
||||
cy.get('[data-testid=verticalNavigation]').should('not.include.text', 'Session History')
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
});
|
||||
|
|
@ -0,0 +1,463 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeSession from '../../factories/session';
|
||||
import makeFakeUser from '../../factories/user';
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
describe('Browse sessions', () => {
|
||||
const currentUser = makeFakeUser();
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: currentUser.id });
|
||||
});
|
||||
|
||||
describe('when there are no active sessions', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, {
|
||||
body: []
|
||||
});
|
||||
cy.visit('/sessions');
|
||||
});
|
||||
|
||||
it('alerts when there is no records', () => {
|
||||
cy.contains('There are no public, open sessions currently available for you to join');
|
||||
cy.contains('create a session').click();
|
||||
cy.url().should('include', '/sessions/new');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are active sessions', () => {
|
||||
describe('in desktop', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('macbook-15');
|
||||
});
|
||||
|
||||
it('lists the sessions', () => {
|
||||
const session = makeFakeSession({
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').should('have.length', 1);
|
||||
});
|
||||
|
||||
describe('session description', () => {
|
||||
it('when user has provided a description', () => {
|
||||
const session = makeFakeSession({
|
||||
description: 'custom description',
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('custom description');
|
||||
});
|
||||
|
||||
it('when user has not provided a description and session is public', () => {
|
||||
const session = makeFakeSession({
|
||||
description: null,
|
||||
musician_access: true,
|
||||
approval_required: false,
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('Public, open session. Feel free to join!');
|
||||
});
|
||||
|
||||
it('when user has not provided a description and session is private and requires approval to join', () => {
|
||||
const session = makeFakeSession({
|
||||
description: null,
|
||||
musician_access: true,
|
||||
approval_required: true,
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains(
|
||||
'Private session. Click the enter button in the right column to request to join'
|
||||
);
|
||||
});
|
||||
|
||||
it('when user has not provided a description and session is RSVP', () => {
|
||||
const session = makeFakeSession({
|
||||
description: null,
|
||||
musician_access: false,
|
||||
approval_required: false,
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('Only RSVP musicians may join');
|
||||
});
|
||||
});
|
||||
|
||||
describe('invitation', () => {
|
||||
it('shows invite detail if the user has been invited', () => {
|
||||
const session = makeFakeSession({
|
||||
invitations: [
|
||||
{
|
||||
sender_id: '1',
|
||||
receiver_id: currentUser.id
|
||||
}
|
||||
],
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('YOU WERE INVITED TO THIS SESSION');
|
||||
});
|
||||
});
|
||||
|
||||
describe('friend info', () => {
|
||||
it('shows if there is a friend in session', () => {
|
||||
const session = makeFakeSession({
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
is_friend: true,
|
||||
name: faker.person.fullName()
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
//console.log('_DEBUG_ session', session);
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('YOU HAVE A FRIEND IN THIS SESSION');
|
||||
});
|
||||
});
|
||||
|
||||
describe('participants', () => {
|
||||
it('shows the participants', () => {
|
||||
const session = makeFakeSession({
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('John Doe');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr').contains('Ray Charles');
|
||||
});
|
||||
});
|
||||
|
||||
describe('instruments', () => {
|
||||
it('shows the instruments', () => {
|
||||
const session = makeFakeSession({
|
||||
participants: [
|
||||
{
|
||||
id: 'p1',
|
||||
user: {
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Electric Guitar',
|
||||
instrument_id: 'electric_guitar'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Drums',
|
||||
instrument_id: 'drums'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'p2',
|
||||
user: {
|
||||
name: 'Ray Charles'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '2',
|
||||
instrument: 'Bass Guitar',
|
||||
instrument_id: 'bass_guitar'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr')
|
||||
.find(`[data-testid=Participantp1Tracks]`).trigger('mouseover')
|
||||
//cy.contains('Electric Guitar');
|
||||
// cy.get('[data-testid=sessionsListTable] tbody tr')
|
||||
// .find(`[data-testid=Participantp1Tracks]`)
|
||||
// .contains('Drums');
|
||||
// cy.get('[data-testid=sessionsListTable] tbody tr')
|
||||
// .find(`[data-testid=Participantp2Tracks]`)
|
||||
// .contains('Bass');
|
||||
});
|
||||
});
|
||||
|
||||
describe('click join button - on a private session', () => {
|
||||
it('shows toast message', () => {
|
||||
const session = makeFakeSession({
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
id: '1',
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
musician_access: true,
|
||||
approval_required: true
|
||||
});
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.intercept('GET', /\S+\/api\/users\/\S+\/latencies\?\S+/, { users: [{
|
||||
user_id: currentUser.id,
|
||||
first_name:"John",
|
||||
last_name:"Doe",
|
||||
audio_latency:5,
|
||||
audio_latency_unknown:false,
|
||||
}]});
|
||||
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsListTable] tbody tr')
|
||||
.find('[data-testid=joinBtn]')
|
||||
.click();
|
||||
cy.contains('You have requested to join this private session.');
|
||||
// cy.get('[data-testid=native-app-unavailable]').contains('Cancel').click();
|
||||
});
|
||||
});
|
||||
|
||||
describe('click join button - on a public session', () => {
|
||||
it('opens native client', () => {
|
||||
const session = makeFakeSession({
|
||||
participants: [
|
||||
{
|
||||
user: {
|
||||
id: '1',
|
||||
name: 'John Doe'
|
||||
},
|
||||
tracks: [
|
||||
{
|
||||
id: '1',
|
||||
instrument: 'Guitar'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
musician_access: true,
|
||||
approval_required: false
|
||||
});
|
||||
const newUrl = `jamkazam://url=http://www.jamkazam.local:3000/client#/findSession/custom~yes%7CjoinSessionId~${
|
||||
session.id
|
||||
}`;
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, [session]);
|
||||
cy.intercept('GET', /\S+\/api\/users\/\S+\/latencies\?\S+/, { users: [{
|
||||
user_id: currentUser.id,
|
||||
first_name:"John",
|
||||
last_name:"Doe",
|
||||
audio_latency:5,
|
||||
audio_latency_unknown:false,
|
||||
}]});
|
||||
cy.visit('/sessions').then(win => {
|
||||
cy.stub(win, 'open').as('windowOpen');
|
||||
});
|
||||
// cy.get('[data-testid=sessionsListTable] tbody tr')
|
||||
// .find('[data-testid=joinBtn]')
|
||||
// .click();
|
||||
//cy.get('@windowOpen').should('have.been.calledWith', newUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('in mobile', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('iphone-6');
|
||||
});
|
||||
|
||||
describe('pagination', () => {
|
||||
it('shows the next page of sessions', () => {
|
||||
const sessions = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
sessions.push(makeFakeSession({ id: faker.string.uuid() }));
|
||||
}
|
||||
cy.intercept('GET', /\S+\/api\/sessions/, sessions);
|
||||
cy.visit('/sessions');
|
||||
cy.get('[data-testid=sessionsSwiper] .swiper-button-prev').should('have.class', 'swiper-button-disabled');
|
||||
for (let i = 0; i < 4; i++) {
|
||||
// 4 because the first one is already visible
|
||||
cy.get('[data-testid=sessionsSwiper] .swiper-button-next').click();
|
||||
cy.wait(500);
|
||||
}
|
||||
cy.get('[data-testid=sessionsSwiper] .swiper-button-next').should('have.class', 'swiper-button-disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import useNativeAppCheck from '../../../src/hooks/useNativeAppCheck';
|
||||
|
||||
describe('Create new session', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: '6' });
|
||||
cy.intercept('GET', /\S+\/users\/\d+\/friends/, { fixture: 'friends' }).as('friends');
|
||||
|
||||
});
|
||||
|
||||
it("adds invitees - using autocomplete list click", () => {
|
||||
cy.visit('/sessions/new');
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
cy.get('[data-testid=autocomplete-list] li').should('have.length', 2)
|
||||
cy.get('[data-testid=autocomplete-list] li:first').click()
|
||||
cy.get('[data-testid=selected-invitees]').children().first().contains('David Wilson')
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
cy.get('[data-testid=autocomplete-list] li').should('have.length', 1)
|
||||
cy.get('[data-testid=autocomplete-list] li').should('not.contain', 'David Wilson')
|
||||
});
|
||||
|
||||
//skipping this test for now. according to the html specification, when there is a single text field in a form
|
||||
//the behaviour is to submit the form on hitting the enter key. need to figureout a way to prevent this.
|
||||
//https://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2
|
||||
it.skip("adds invitees using autocomplete enter", () => {
|
||||
cy.visit('/sessions/new');
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
cy.get('[data-testid=autocomplete-list] li').should('have.length', 2)
|
||||
cy.get('[data-testid=autocomplete-text]').type('{enter}')
|
||||
cy.get('[data-testid=selected-invitees]').children().first().contains('David Wilson')
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
cy.get('[data-testid=autocomplete-list] li').should('have.length', 1)
|
||||
cy.get('[data-testid=autocomplete-list] li').should('not.contain', 'David Wilson')
|
||||
});
|
||||
|
||||
it("removes invitees", () => {
|
||||
cy.visit('/sessions/new');
|
||||
cy.get('[data-testid=autocomplete-text]').type('Seth')
|
||||
cy.get('[data-testid=autocomplete-list] li').should('have.length', 1)
|
||||
cy.get('[data-testid=autocomplete-list] li:first').click()
|
||||
cy.get('[data-testid=selected-invitees]').children().first().contains('Seth Call')
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
cy.get('[data-testid=autocomplete-list] li').should('have.length', 2)
|
||||
cy.get('[data-testid=autocomplete-list] li:first').click()
|
||||
cy.get('[data-testid=selected-invitees]').children().first().find('a').click()
|
||||
cy.get('[data-testid=selected-invitees]').children().should('have.length', 1)
|
||||
cy.get('[data-testid=selected-invitees]').children().first().find('a').click()
|
||||
cy.get('[data-testid=selected-invitees]').should('not.exist')
|
||||
});
|
||||
|
||||
it("choose friends as invitees", ()=> {
|
||||
cy.visit('/sessions/new');
|
||||
cy.get('[data-testid=btn-choose-friends]').click();
|
||||
cy.get('[data-testid=modal-choose-friends]').should('be.visible').contains('Invite Friends to Session')
|
||||
cy.get('[data-testid=modal-choose-friends]').find('[type="checkbox"]').first().click()
|
||||
cy.get('[data-testid=modal-choose-friends]').contains('Add Friends').click()
|
||||
cy.get('[data-testid=selected-invitees]').children().first().contains('Seth Call')
|
||||
cy.get('[data-testid=btn-choose-friends]').click();
|
||||
cy.get('[data-testid=modal-choose-friends]').should('not.contain', 'Seth Call')
|
||||
})
|
||||
|
||||
it.only("prefill form using saved data in localStorage", () => {
|
||||
cy.visit('/sessions/new');
|
||||
cy.get('[data-testid=session-privacy]').select("2")
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
cy.get('[data-testid=autocomplete-list] li:first').click()
|
||||
cy.get('[data-testid=session-description]').type("My test session")
|
||||
cy.get('[data-testid=btn-create-session]').click();
|
||||
cy.reload() //refresh browser
|
||||
cy.get('[data-testid=session-privacy]').should('have.value', "2")
|
||||
cy.get('[data-testid=selected-invitees]').children().first().contains('David Wilson')
|
||||
cy.get('[data-testid=session-description]').contains('My test session')
|
||||
})
|
||||
|
||||
it("submits form", () => {
|
||||
const newUrl = `jamkazam://url=http://www.jamkazam.local:3000/client#/createSession/custom~yes%7Cprivacy~2%7Cdescription~test%7CinviteeIds~1`;
|
||||
cy.visit('/sessions/new').then((win) => {
|
||||
cy.stub(win, 'open').as('windowOpen');
|
||||
});
|
||||
cy.get('[data-testid=session-privacy]').select("2")
|
||||
cy.get('[data-testid=autocomplete-text]').type('Seth')
|
||||
cy.get('[data-testid=autocomplete-list] li:first').click()
|
||||
cy.get('[data-testid=session-description]').type("test")
|
||||
cy.get('[data-testid=btn-create-session]').click();
|
||||
cy.get('@windowOpen').should('have.been.calledWith', newUrl);
|
||||
cy.get('[data-testid=btn-create-session]').should('be.disabled')
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import makeFakeUser from '../../factories/user';
|
||||
|
||||
describe('Session History', () => {
|
||||
const currentUser = makeFakeUser();
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: currentUser.id });
|
||||
});
|
||||
|
||||
describe('When user has no sessions', () => {
|
||||
it('should show no sessions message', () => {
|
||||
cy.intercept('GET', /\S+\/api\/sessions\/history/, {
|
||||
body: []
|
||||
}).as('fetchAllSessions')
|
||||
cy.visit('/sessions/history');
|
||||
cy.wait('@fetchAllSessions');
|
||||
cy.contains('No Records!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import { mergePartially, NestedPartial } from 'merge-partially';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { IUser } from './user';
|
||||
|
||||
interface ITrack {
|
||||
id: string;
|
||||
instrument: string;
|
||||
}
|
||||
|
||||
interface IParticipant {
|
||||
id: string;
|
||||
client_id: string;
|
||||
user: IUser;
|
||||
tracks: ITrack[];
|
||||
}
|
||||
|
||||
interface IInvitations {
|
||||
id: string;
|
||||
sender_id?: string;
|
||||
receiver_id?: string;
|
||||
}
|
||||
|
||||
interface ISession {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
participants?: IParticipant[];
|
||||
invitations?: IInvitations[];
|
||||
}
|
||||
|
||||
export default function makeFakeSession(overrides?: NestedPartial<ISession>): ISession {
|
||||
return mergePartially.deep(
|
||||
{
|
||||
id: faker.string.uuid(),
|
||||
name: faker.lorem.sentence({ min: 3, max: 5 }),
|
||||
description: faker.lorem.paragraph(),
|
||||
participants: [
|
||||
{
|
||||
id: faker.string.uuid(),
|
||||
client_id: faker.string.uuid(),
|
||||
user: {
|
||||
id: faker.string.uuid(),
|
||||
},
|
||||
tracks: [{
|
||||
id: faker.string.uuid(),
|
||||
instrument: "Piano"
|
||||
}]
|
||||
}],
|
||||
invitations: [
|
||||
{
|
||||
id: faker.string.uuid()
|
||||
}
|
||||
]
|
||||
},
|
||||
overrides
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { mergePartially, NestedPartial } from 'merge-partially';
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
export interface IUser {
|
||||
id: string;
|
||||
name?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
city?: string;
|
||||
state?: string;
|
||||
country?: string;
|
||||
biography?: string;
|
||||
online?: boolean;
|
||||
musician?: boolean;
|
||||
photo_url?: string;
|
||||
}
|
||||
|
||||
export default function makeFakeUser(overrides?: NestedPartial<IUser>): IUser {
|
||||
const fname: string = faker.person.firstName();
|
||||
const lname: string = faker.person.lastName();
|
||||
return mergePartially.deep(
|
||||
{
|
||||
id: faker.string.uuid(),
|
||||
email: faker.internet.email(),
|
||||
firstName: fname,
|
||||
lastName: lname,
|
||||
name: fname + ' ' + lname,
|
||||
},
|
||||
overrides
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
[
|
||||
{
|
||||
"id": "1",
|
||||
"first_name": "Seth",
|
||||
"last_name": "Call"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"first_name": "David",
|
||||
"last_name": "Wilson"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"first_name": "David",
|
||||
"last_name": "Cluff"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"first_name": "Bob",
|
||||
"last_name": "Merrill"
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"first_name": "Jorge",
|
||||
"last_name": "Lopez"
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
"first_name": "Jorge",
|
||||
"last_name": "Lopez"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,562 @@
|
|||
|
||||
{
|
||||
"next": 10,
|
||||
"count": 3756,
|
||||
"jamtracks": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Back in Black",
|
||||
"description": "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the AC DC song \"Back in Black\".",
|
||||
"recording_type": "Cover",
|
||||
"original_artist": "AC DC",
|
||||
"songwriter": null,
|
||||
"publisher": null,
|
||||
"sales_region": "Worldwide",
|
||||
"price": "1.99",
|
||||
"version": "0",
|
||||
"duration": 221,
|
||||
"year": null,
|
||||
"plan_code": "jamtrack-acdc-backinblack",
|
||||
"allow_free": true,
|
||||
"download_price": "2.99",
|
||||
"upgrade_price": "1.0",
|
||||
"tracks": [
|
||||
{
|
||||
"id": "103dea4d-f2a3-4414-8efe-d2ca378dda60",
|
||||
"part": "Master Mix",
|
||||
"instrument": {
|
||||
"id": "computer",
|
||||
"description": "Computer",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Master",
|
||||
"position": 1000,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Master%20Mix-44100-preview-e9a5a63f34b4d523ee1842fff31f88ce.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Master%20Mix-44100-preview-25fcba7ace7086e3cb6b97d7e33ba72e.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Master%20Mix-44100-preview-9f0b072ed9f4b546e170fcdfb302137e.mp3"
|
||||
},
|
||||
{
|
||||
"id": "2755cbdd-0476-4f3b-9ba1-e2da561ddb4e",
|
||||
"part": "Lead",
|
||||
"instrument": {
|
||||
"id": "voice",
|
||||
"description": "Voice",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 1,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Vocal%20-%20Lead-44100-preview-d35c328fc3936dad9a79fe102dc72950.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Vocal%20-%20Lead-44100-preview-b97b37651eae352fae3b3060918c7bcb.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Vocal%20-%20Lead-44100-preview-d35c328fc3936dad9a79fe102dc72950.aac"
|
||||
},
|
||||
{
|
||||
"id": "0db7c4e1-5e8d-43fe-bd35-98acd8f68b26",
|
||||
"part": "Drums",
|
||||
"instrument": {
|
||||
"id": "drums",
|
||||
"description": "Drums",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 2,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Drums-44100-preview-03aadceb966caf40b96334bdd00234f6.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Drums-44100-preview-854914e3e0d6fdc5f0794325b0ecaead.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Drums-44100-preview-03aadceb966caf40b96334bdd00234f6.aac"
|
||||
},
|
||||
{
|
||||
"id": "2cc79ab6-dab8-4905-85e6-0df5f8e087f1",
|
||||
"part": "Bass",
|
||||
"instrument": {
|
||||
"id": "bass guitar",
|
||||
"description": "Bass Guitar",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 3,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Bass-44100-preview-61c334ac87f811bd010ed3a910764c2e.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Bass-44100-preview-4066dafd7b72e9993b0c0fe1dba2b332.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Bass-44100-preview-61c334ac87f811bd010ed3a910764c2e.aac"
|
||||
},
|
||||
{
|
||||
"id": "ed1d3487-3b32-442f-9c76-8a36fe3bb643",
|
||||
"part": "Solo",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 4,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Solo-44100-preview-e9fe8572a9ac1022762642cbd92b3c34.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Solo-44100-preview-5fb058042254206cfa9fb4dcb0310b2c.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Solo-44100-preview-e9fe8572a9ac1022762642cbd92b3c34.aac"
|
||||
},
|
||||
{
|
||||
"id": "f4ce7c91-7542-4e03-8fc2-68b31683d33e",
|
||||
"part": "Rhythm 1",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 5,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%201-44100-preview-6b498479823d4131a01fa535817d5eab.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%201-44100-preview-f4cbb31dbde3e1a3e6012730a7e0e10f.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%201-44100-preview-6b498479823d4131a01fa535817d5eab.aac"
|
||||
},
|
||||
{
|
||||
"id": "2d96c7ec-59f1-4d56-8a7f-7f4c75a0ccef",
|
||||
"part": "Rhythm 2",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 6,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%202-44100-preview-a626d7c632560f6737e1b6024141289e.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%202-44100-preview-06a0e5af451f001f3465992efcd34ec0.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%202-44100-preview-a626d7c632560f6737e1b6024141289e.aac"
|
||||
},
|
||||
{
|
||||
"id": "fce018ca-c897-4137-aa10-ef56a8e1831f",
|
||||
"part": "Intro Scrapes",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 7,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Intro%20Scrapes-44100-preview-0ddfaa7154e9ba35d05d60477d5dd3e9.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Intro%20Scrapes-44100-preview-f53ce3c5f9dcf81af51560f52635fbb0.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Intro%20Scrapes-44100-preview-0ddfaa7154e9ba35d05d60477d5dd3e9.aac"
|
||||
},
|
||||
{
|
||||
"id": "c9b3e0a8-4db0-4d0f-9769-398a6d56506e",
|
||||
"part": "Main",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 8,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Main-44100-preview-234a224f75a97d7ff8f55442ece6fcde.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Main-44100-preview-828c9691f5435dea1c90182fa2618c9b.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Main-44100-preview-234a224f75a97d7ff8f55442ece6fcde.aac"
|
||||
},
|
||||
{
|
||||
"id": "28c3df07-2a88-45a9-9ae6-3399a5d2eb20",
|
||||
"part": "Sound FX",
|
||||
"instrument": {
|
||||
"id": "computer",
|
||||
"description": "Computer",
|
||||
"created_at": "2021-02-02T23:16:46.168Z",
|
||||
"updated_at": "2021-02-02T23:16:46.168Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 9,
|
||||
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Sound%20Effects-44100-preview-6c859c73036cd55bceb65f19f2d2f2f3.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Sound%20Effects-44100-preview-f840d8df4c7388f776477139025ee712.ogg",
|
||||
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Sound%20Effects-44100-preview-6c859c73036cd55bceb65f19f2d2f2f3.aac"
|
||||
}
|
||||
],
|
||||
"licensor": null,
|
||||
"genres": ["Rock", "Pop"],
|
||||
"added_cart": true,
|
||||
"can_download": true,
|
||||
"purchased": true
|
||||
},
|
||||
{
|
||||
"id": "531",
|
||||
"name": "1234",
|
||||
"description": "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the Feist song \"1234\".",
|
||||
"recording_type": "Cover",
|
||||
"original_artist": "Feist",
|
||||
"songwriter": null,
|
||||
"publisher": null,
|
||||
"sales_region": "Worldwide",
|
||||
"price": "1.99",
|
||||
"version": "1",
|
||||
"duration": 184,
|
||||
"year": 2007,
|
||||
"plan_code": "jamtrack-feist-1234",
|
||||
"allow_free": true,
|
||||
"download_price": "2.99",
|
||||
"upgrade_price": "1.0",
|
||||
"tracks": [
|
||||
{
|
||||
"id": "b834ce9c-2624-4977-a079-0b1b5d90ad6e",
|
||||
"part": "Clicktrack",
|
||||
"instrument": {
|
||||
"id": "computer",
|
||||
"description": "Computer",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Click",
|
||||
"position": 10000,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234%20-%20Tency%20Music/1234%20Clicktrack-44100-preview-90bed87ea6402ab0f8d283ffe094c95f.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234%20-%20Tency%20Music/1234%20Clicktrack-44100-preview-7198bae1519e40827aff0bd704e2066b.ogg",
|
||||
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234%20-%20Tency%20Music/1234%20Clicktrack-44100-preview-90bed87ea6402ab0f8d283ffe094c95f.aac"
|
||||
},
|
||||
{
|
||||
"id": "b5a67d64-f94f-453e-9dab-691304275bc2",
|
||||
"part": "Master Mix",
|
||||
"instrument": {
|
||||
"id": "computer",
|
||||
"description": "Computer",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Master",
|
||||
"position": 1000,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1%202%203%204%20Master%20Mix-44100-preview-5fe2a923a614f17dd9c6440b65c1e884.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1%202%203%204%20Master%20Mix-44100-preview-aaefd823d7deaa1bc862eb8ec1e53fe3.ogg",
|
||||
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1%202%203%204%20Master%20Mix-44100-preview-6d3edecd174080e88e611984969f1b2d.aac"
|
||||
},
|
||||
{
|
||||
"id": "27353eb1-9f2b-487b-9047-93f49446b4db",
|
||||
"part": "Lead",
|
||||
"instrument": {
|
||||
"id": "voice",
|
||||
"description": "Voice",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 1,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Lead-44100-preview-426d923380d10fbdf2d5a4e4108b0de2.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Lead-44100-preview-30eaef3aea1499493cbad76c5c3c8a50.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "78736d23-e657-4922-b145-9f7db40bb36e",
|
||||
"part": "Backing",
|
||||
"instrument": {
|
||||
"id": "voice",
|
||||
"description": "Voice",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 2,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Backing-44100-preview-c0fb7721f3c8cd3253c8c5a6d6b034dd.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Backing-44100-preview-b7145437bbb6b01b566f10ef9009d001.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "1978c439-115b-4214-85a6-beec780310a1",
|
||||
"part": "Drums",
|
||||
"instrument": {
|
||||
"id": "drums",
|
||||
"description": "Drums",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 3,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Drums%20-%20Drums-44100-preview-da3d7e78b9a7b50bea7bcc8f13a7d4b0.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Drums%20-%20Drums-44100-preview-d320dc7600c6d9c08af19b813d6d8176.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "8457c55d-924d-4048-8c5f-46b5c7668552",
|
||||
"part": "Bass",
|
||||
"instrument": {
|
||||
"id": "bass guitar",
|
||||
"description": "Bass Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 4,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Bass%20Guitar%20-%20Bass-44100-preview-c4b1ce442a9645f6ead0f078afe48d3d.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Bass%20Guitar%20-%20Bass-44100-preview-8a0b0d664802015a219f3320d4d5c0cd.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "ca5d5597-1a19-45b1-9d1b-478f6b6c4b19",
|
||||
"part": "Piano",
|
||||
"instrument": {
|
||||
"id": "piano",
|
||||
"description": "Piano",
|
||||
"created_at": "2014-02-16T13:10:07.059Z",
|
||||
"updated_at": "2014-02-16T13:10:07.059Z",
|
||||
"popularity": 2
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 5,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Piano%20-%20Piano-44100-preview-208fb9e053bb445c1d55ec443809211c.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Piano%20-%20Piano-44100-preview-8940844b3972b39ad9b786805496bc1b.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "755c6c48-fc6c-4dc8-bbf2-9dc0d0a5f2a8",
|
||||
"part": "Acoustic",
|
||||
"instrument": {
|
||||
"id": "acoustic guitar",
|
||||
"description": "Acoustic Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 6,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Acoustic%20Guitar%20-%20Acoustic-44100-preview-11bde0963b7a833072a49c1096a0b6ef.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Acoustic%20Guitar%20-%20Acoustic-44100-preview-92c44a32feac702880796fd9d6d5f883.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "508bcbaa-4d29-44c0-8cdd-f57fd3fee717",
|
||||
"part": "Banjo",
|
||||
"instrument": {
|
||||
"id": "banjo",
|
||||
"description": "Banjo",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 2
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 7,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Banjo%20-%20Banjo-44100-preview-6ed9db0acb0616651e297b0c057f453d.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Banjo%20-%20Banjo-44100-preview-1e18e0b352e4b05fefa35ce7fd9c84a1.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "6ef8af53-6615-450c-b78b-4310fb0e0395",
|
||||
"part": "Strings",
|
||||
"instrument": {
|
||||
"id": "orchestra",
|
||||
"description": "Orchestra",
|
||||
"created_at": "2015-08-11T16:08:58.806Z",
|
||||
"updated_at": "2015-08-11T16:08:58.806Z",
|
||||
"popularity": 1
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 8,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Orchestra%20-%20Strings-44100-preview-7017822e5edaedbe91fea259cc29666c.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Orchestra%20-%20Strings-44100-preview-70f5d96ed08b1d6a625f21c8be3f691c.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "91427115-63be-49e6-adb1-078f4a7f7ae8",
|
||||
"part": "Trumpet",
|
||||
"instrument": {
|
||||
"id": "trumpet",
|
||||
"description": "Trumpet",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 2
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 9,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Trumpet%20-%20Trumpet-44100-preview-74d7ab8c5af40dc9768176df2afd691a.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Trumpet%20-%20Trumpet-44100-preview-df093dfebc96d3c22c13ed8d14b44ed0.ogg",
|
||||
"preview_aac_url": null
|
||||
}
|
||||
],
|
||||
"licensor": { "id": "027d90a1-b126-4d5a-8af6-3167296dfb04", "name": "Tency Music" },
|
||||
"genres": ["Folk", "Alternative Rock"],
|
||||
"added_cart": false,
|
||||
"can_download": false,
|
||||
"purchased": false
|
||||
},
|
||||
{
|
||||
"id": "1437",
|
||||
"name": "18 And Life",
|
||||
"description": "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the Skid Row song \"18 And Life\".",
|
||||
"recording_type": "Cover",
|
||||
"original_artist": "Skid Row",
|
||||
"songwriter": null,
|
||||
"publisher": null,
|
||||
"sales_region": "Worldwide",
|
||||
"price": "1.99",
|
||||
"version": "1",
|
||||
"duration": 227,
|
||||
"year": 1989,
|
||||
"plan_code": "jamtrack-skidrow-18andlife",
|
||||
"allow_free": true,
|
||||
"download_price": "2.99",
|
||||
"upgrade_price": "1.0",
|
||||
"tracks": [
|
||||
{
|
||||
"id": "7c515f02-bebd-4fdd-b30a-81b72ec277b9",
|
||||
"part": "Clicktrack",
|
||||
"instrument": {
|
||||
"id": "computer",
|
||||
"description": "Computer",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Click",
|
||||
"position": 10000,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life%20-%20Tency%20Music/click-44100-preview-424086caaa67c89532635ef0970b2d75.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life%20-%20Tency%20Music/click-44100-preview-d30de7c1353826896110d3ce5f459b23.ogg",
|
||||
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life%20-%20Tency%20Music/click-44100-preview-424086caaa67c89532635ef0970b2d75.aac"
|
||||
},
|
||||
{
|
||||
"id": "db0a34e4-71e9-4342-b04a-e7c7f068b4fb",
|
||||
"part": "Master Mix",
|
||||
"instrument": {
|
||||
"id": "computer",
|
||||
"description": "Computer",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Master",
|
||||
"position": 1000,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Master%20Mix-44100-preview-5bf5b2872e898dc43875f829e238b62b.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Master%20Mix-44100-preview-3ba1eb2b8ab936212cc299cfb8db34ac.ogg",
|
||||
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Master%20Mix-44100-preview-146e9a850f83a2f76dc31dc9489dc3aa.aac"
|
||||
},
|
||||
{
|
||||
"id": "cc237bfd-5d87-413b-a460-55d17594f785",
|
||||
"part": "Lead",
|
||||
"instrument": {
|
||||
"id": "voice",
|
||||
"description": "Voice",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 1,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/ld-44100-preview-04a953ee8607a97e8533ea5adf4560a1.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/ld-44100-preview-3486ef50eb889b71208040a0a13de52f.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "a182cf33-1d61-41a8-8e9b-fd23d501885c",
|
||||
"part": "Drums",
|
||||
"instrument": {
|
||||
"id": "drums",
|
||||
"description": "Drums",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 2,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/drums-44100-preview-7ec143f7aa0dc019a2af48c6861c400b.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/drums-44100-preview-6fd8eacb6a8bd35a85e0d4a3a8ec120c.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "406c1f87-c6c1-406f-85a9-05768267ff46",
|
||||
"part": "Bass",
|
||||
"instrument": {
|
||||
"id": "bass guitar",
|
||||
"description": "Bass Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 3,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/bass-44100-preview-37326a28b8d55f4b61a00eb7406a4da9.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/bass-44100-preview-ff2e87a89ae7693fa8f15a3d065c5833.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "fa46fcd7-0647-4f94-93fd-bddb403abc8b",
|
||||
"part": "Lead",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 4,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Lead-44100-preview-7b8ac822d2a0ccf341c4607919e5125a.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Lead-44100-preview-3d13650573ebe20f939a7e2d661f9fab.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "42e6c71e-715c-48b9-b000-fb7fdabd3fe3",
|
||||
"part": "Rhythm Distorted",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 5,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%20Distorted-44100-preview-4f0312e6d429fa211c873ecd671c2629.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%20Distorted-44100-preview-36e75c706ff27930a54be1159afafd9a.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "a16a69b1-2b89-4bca-8039-bb927c7f517f",
|
||||
"part": "Arpeggios Clean",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 6,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Clean-44100-preview-f1f3df4288f9ac9bd8183303099f1a40.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Clean-44100-preview-f8da9761dbed9afd32d636fb0ba2f757.ogg",
|
||||
"preview_aac_url": null
|
||||
},
|
||||
{
|
||||
"id": "8246f3fb-6d35-4a31-9bcd-ef81fd39c66f",
|
||||
"part": "Arpeggios Distorted",
|
||||
"instrument": {
|
||||
"id": "electric guitar",
|
||||
"description": "Electric Guitar",
|
||||
"created_at": "2013-01-03T01:57:43.040Z",
|
||||
"updated_at": "2013-01-03T01:57:43.040Z",
|
||||
"popularity": 3
|
||||
},
|
||||
"track_type": "Track",
|
||||
"position": 7,
|
||||
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Distorted-44100-preview-2bf523d0f429eaff366691fff0dbcb5f.mp3",
|
||||
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Distorted-44100-preview-e25718a81d9d0a011746def84547f1e9.ogg",
|
||||
"preview_aac_url": null
|
||||
}
|
||||
],
|
||||
"licensor": { "id": "027d90a1-b126-4d5a-8af6-3167296dfb04", "name": "Tency Music" },
|
||||
"genres": ["Metal", "Rock", "Hard Rock"],
|
||||
"added_cart": false,
|
||||
"can_download": false,
|
||||
"purchased": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,74 +1,81 @@
|
|||
[
|
||||
{
|
||||
"description": "TEXT_MESSAGE",
|
||||
"source_user_id": "27bd4a30-d1b8-4eea-8454-01a104d59381",
|
||||
"target_user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
|
||||
"session_id": null,
|
||||
"recording_id": null,
|
||||
"invitation_id": null,
|
||||
"join_request_id": null,
|
||||
"friend_request_id": null,
|
||||
"band_id": null,
|
||||
"band_invitation_id": null,
|
||||
"formatted_msg": "TEXT_MESSAGE",
|
||||
"message": "Hello",
|
||||
"created_at": "2021-10-07T00:09:57.704Z",
|
||||
"lesson_session_id": null,
|
||||
"purpose": null,
|
||||
"source_user": {
|
||||
"name": "Nuwan Chaturanga"
|
||||
{
|
||||
"next": null,
|
||||
"unread_total": 3,
|
||||
"notifications": [
|
||||
{
|
||||
"description": "TEXT_MESSAGE",
|
||||
"source_user_id": "27bd4a30-d1b8-4eea-8454-01a104d59381",
|
||||
"target_user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
|
||||
"session_id": null,
|
||||
"recording_id": null,
|
||||
"invitation_id": null,
|
||||
"join_request_id": null,
|
||||
"friend_request_id": null,
|
||||
"band_id": null,
|
||||
"band_invitation_id": null,
|
||||
"formatted_msg": "TEXT_MESSAGE",
|
||||
"message": "Hello",
|
||||
"created_at": "2021-10-07T00:09:57.704Z",
|
||||
"lesson_session_id": null,
|
||||
"purpose": null,
|
||||
"source_user": {
|
||||
"name": "Nuwan Chaturanga"
|
||||
},
|
||||
"notification_id": "63fcd878-9a22-4419-9cee-8a51a615da97",
|
||||
"fan_access": null,
|
||||
"musician_access": null,
|
||||
"approval_required": null,
|
||||
"read_at": null
|
||||
},
|
||||
"notification_id": "63fcd878-9a22-4419-9cee-8a51a615da97",
|
||||
"fan_access": null,
|
||||
"musician_access": null,
|
||||
"approval_required": null
|
||||
},
|
||||
{
|
||||
"description": "FRIEND_REQUEST",
|
||||
"source_user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
|
||||
"target_user_id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
|
||||
"session_id": null,
|
||||
"recording_id": null,
|
||||
"invitation_id": null,
|
||||
"join_request_id": null,
|
||||
"friend_request_id": "7c842904-24f5-4515-8886-0c3d25ee641b",
|
||||
"band_id": null,
|
||||
"band_invitation_id": null,
|
||||
"formatted_msg": "Seth Call has sent you a friend request.",
|
||||
"message": null,
|
||||
"created_at": "2021-10-15T05:36:48.527Z",
|
||||
"lesson_session_id": null,
|
||||
"purpose": null,
|
||||
"source_user": {
|
||||
"name": "Seth Call"
|
||||
{
|
||||
"description": "FRIEND_REQUEST",
|
||||
"source_user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
|
||||
"target_user_id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
|
||||
"session_id": null,
|
||||
"recording_id": null,
|
||||
"invitation_id": null,
|
||||
"join_request_id": null,
|
||||
"friend_request_id": "7c842904-24f5-4515-8886-0c3d25ee641b",
|
||||
"band_id": null,
|
||||
"band_invitation_id": null,
|
||||
"formatted_msg": "Seth Call has sent you a friend request.",
|
||||
"message": null,
|
||||
"created_at": "2021-10-15T05:36:48.527Z",
|
||||
"lesson_session_id": null,
|
||||
"purpose": null,
|
||||
"source_user": {
|
||||
"name": "Seth Call"
|
||||
},
|
||||
"notification_id": "3364b5f1-8946-46a3-b635-86d89d237849",
|
||||
"fan_access": null,
|
||||
"musician_access": null,
|
||||
"approval_required": null,
|
||||
"read_at": null
|
||||
},
|
||||
"notification_id": "3364b5f1-8946-46a3-b635-86d89d237849",
|
||||
"fan_access": null,
|
||||
"musician_access": null,
|
||||
"approval_required": null
|
||||
},
|
||||
{
|
||||
"description": "FRIEND_REQUEST_ACCEPTED",
|
||||
"source_user_id": "29becbf4-8be5-4078-9405-0edadc9fa42d",
|
||||
"target_user_id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
|
||||
"session_id": null,
|
||||
"recording_id": null,
|
||||
"invitation_id": null,
|
||||
"join_request_id": null,
|
||||
"friend_request_id": null,
|
||||
"band_id": null,
|
||||
"band_invitation_id": null,
|
||||
"formatted_msg": "Peter Walker has accepted your friend request.",
|
||||
"message": null,
|
||||
"created_at": "2021-10-05T12:38:53.134Z",
|
||||
"lesson_session_id": null,
|
||||
"purpose": null,
|
||||
"source_user": {
|
||||
"name": "Peter Walker"
|
||||
},
|
||||
"notification_id": "bb9269f3-721c-48cd-9bf6-bcff72877198",
|
||||
"fan_access": null,
|
||||
"musician_access": null,
|
||||
"approval_required": null
|
||||
}
|
||||
]
|
||||
{
|
||||
"description": "FRIEND_REQUEST_ACCEPTED",
|
||||
"source_user_id": "29becbf4-8be5-4078-9405-0edadc9fa42d",
|
||||
"target_user_id": "b1ddadd0-0263-47c4-bf91-e7767f386970",
|
||||
"session_id": null,
|
||||
"recording_id": null,
|
||||
"invitation_id": null,
|
||||
"join_request_id": null,
|
||||
"friend_request_id": null,
|
||||
"band_id": null,
|
||||
"band_invitation_id": null,
|
||||
"formatted_msg": "Peter Walker has accepted your friend request.",
|
||||
"message": null,
|
||||
"created_at": "2021-10-05T12:38:53.134Z",
|
||||
"lesson_session_id": null,
|
||||
"purpose": null,
|
||||
"source_user": {
|
||||
"name": "Peter Walker"
|
||||
},
|
||||
"notification_id": "bb9269f3-721c-48cd-9bf6-bcff72877198",
|
||||
"fan_access": null,
|
||||
"musician_access": null,
|
||||
"approval_required": null,
|
||||
"read_at": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"entries": [
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
|
||||
"created_at": "2022-12-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2",
|
||||
"created_at": "2022-11-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8w9x0",
|
||||
"created_at": "2022-10-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "y1z2a3b4-c5d6-e7f8-g9h0-i1j2k3l4m5n6o7",
|
||||
"created_at": "2022-09-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "p8q9r0s1-t2u3-v4w5-x6y7-z8a9b0c1d2e3f4",
|
||||
"created_at": "2022-08-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "g5h6i7j8-k9l0-m1n2-o3p4-q5r6s7t8u9v0w1x2",
|
||||
"created_at": "2022-07-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "y3z4a5b6-c7d8-e9f0-g1h2-i3j4k5l6m7n8o9",
|
||||
"created_at": "2022-06-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "p2q3r4s5-t6u7-v8w9-x0y1-z2a3b4c5d6e7f8",
|
||||
"created_at": "2022-05-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "g9h0i1j2-k3l4-m5n6-o7p8-q9r0s1t2u3v4w5x6",
|
||||
"created_at": "2022-04-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "y7z8a9b0-c1d2-e3f4-g5h6-i7j8k9l0m1n2o3p4",
|
||||
"created_at": "2022-03-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"entries": [
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
|
||||
"created_at": "2022-02-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2",
|
||||
"created_at": "2022-01-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8w9x0",
|
||||
"created_at": "2021-12-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "y1z2a3b4-c5d6-e7f8-g9h0-i1j2k3l4m5n6o7",
|
||||
"created_at": "2021-11-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "p8q9r0s1-t2u3-v4w5-x6y7-z8a9b0c1d2e3f4",
|
||||
"created_at": "2021-10-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "g5h6i7j8-k9l0-m1n2-o3p4-q5r6s7t8u9v0w1x2",
|
||||
"created_at": "2021-09-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "y3z4a5b6-c7d8-e9f0-g1h2-i3j4k5l6m7n8o9",
|
||||
"created_at": "2021-08-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "p2q3r4s5-t6u7-v8w9-x0y1-z2a3b4c5d6e7f8",
|
||||
"created_at": "2021-07-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "g9h0i1j2-k3l4-m5n6-o7p8-q9r0s1t2u3v4w5x6",
|
||||
"created_at": "2021-06-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "y7z8a9b0-c1d2-e3f4-g5h6-i7j8k9l0m1n2o3p4",
|
||||
"created_at": "2021-05-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
|
||||
"created_at": "2021-04-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"entries": [
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
|
||||
"created_at": "2021-03-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
},
|
||||
{
|
||||
"id": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2",
|
||||
"created_at": "2021-02-01T00:00:00Z",
|
||||
"total_in_cents": 999,
|
||||
"description": "JamKazam Gold Plan - Monthly",
|
||||
"status": "paid"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@
|
|||
{ "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
|
||||
{ "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
|
||||
{ "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 },
|
||||
{ "instrument_id": "banjo", "description": "Banjo", "proficiency_level": 2, "priority": 4}
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": true,
|
||||
|
|
@ -29,7 +30,33 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 10,
|
||||
"audio_latency": 5,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": [
|
||||
{
|
||||
"description": "Asian"
|
||||
},
|
||||
{
|
||||
"description": "Classical"
|
||||
},
|
||||
{
|
||||
"description": "Hard Rock"
|
||||
},
|
||||
{
|
||||
"description": "Jazz"
|
||||
},
|
||||
{
|
||||
"description": "Latin"
|
||||
},
|
||||
{
|
||||
"description": "Oldies"
|
||||
},
|
||||
{
|
||||
"description": "Pop"
|
||||
},
|
||||
{
|
||||
"description": "Soft Rock"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
|
|
@ -56,7 +83,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
|
|
@ -83,7 +111,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
|
|
@ -110,7 +139,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
|
|
@ -137,7 +167,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
|
|
@ -164,7 +195,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
|
|
@ -191,7 +223,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
|
|
@ -218,7 +251,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "9",
|
||||
|
|
@ -245,7 +279,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
|
|
@ -272,7 +307,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -304,7 +340,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 10,
|
||||
"audio_latency": 5,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "12",
|
||||
|
|
@ -331,7 +368,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "13",
|
||||
|
|
@ -358,7 +396,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "14",
|
||||
|
|
@ -385,7 +424,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
|
|
@ -412,7 +452,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
|
|
@ -439,7 +480,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "17",
|
||||
|
|
@ -466,7 +508,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "18",
|
||||
|
|
@ -493,7 +536,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "19",
|
||||
|
|
@ -520,7 +564,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "20",
|
||||
|
|
@ -547,7 +592,8 @@
|
|||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
}
|
||||
],
|
||||
"page_count": 2,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,319 @@
|
|||
{
|
||||
"musicians": [
|
||||
{
|
||||
"id": "1",
|
||||
"first_name": "Test",
|
||||
"last_name": "User1",
|
||||
"name": "Test User1",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"online": true,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "Biography of Test User1. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
|
||||
{ "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
|
||||
{ "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
|
||||
{ "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 },
|
||||
{ "instrument_id": "banjo", "description": "Banjo", "proficiency_level": 2, "priority": 4}
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": true,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 1,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 10,
|
||||
"audio_latency": 5,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": [
|
||||
{
|
||||
"description": "Asian"
|
||||
},
|
||||
{
|
||||
"description": "Classical"
|
||||
},
|
||||
{
|
||||
"description": "Hard Rock"
|
||||
},
|
||||
{
|
||||
"description": "Jazz"
|
||||
},
|
||||
{
|
||||
"description": "Latin"
|
||||
},
|
||||
{
|
||||
"description": "Oldies"
|
||||
},
|
||||
{
|
||||
"description": "Pop"
|
||||
},
|
||||
{
|
||||
"description": "Soft Rock"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"first_name": "Test",
|
||||
"last_name": "User2",
|
||||
"name": "Test User2",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "Biography of Test User2.",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"first_name": "Test",
|
||||
"last_name": "User3",
|
||||
"name": "Test User3",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"first_name": "Test",
|
||||
"last_name": "User4",
|
||||
"name": "Test User4",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"first_name": "Test",
|
||||
"last_name": "User5",
|
||||
"name": "Test User5",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
"first_name": "Test",
|
||||
"last_name": "User6",
|
||||
"name": "Test User6",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
"first_name": "Test",
|
||||
"last_name": "User7",
|
||||
"name": "Test User7",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"first_name": "Test",
|
||||
"last_name": "User8",
|
||||
"name": "Test User8",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "9",
|
||||
"first_name": "Test",
|
||||
"last_name": "User9",
|
||||
"name": "Test User9",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
"first_name": "Test",
|
||||
"last_name": "User10",
|
||||
"name": "Test User10",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
}
|
||||
],
|
||||
"offset": 10,
|
||||
"my_audio_latency": 5,
|
||||
"filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",
|
||||
"description": "Current Search: Sort = Latency to Me",
|
||||
"is_blank_filter": false
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
{
|
||||
"musicians": [
|
||||
{
|
||||
"id": "11",
|
||||
"first_name": "Test",
|
||||
"last_name": "User11",
|
||||
"name": "Test User11",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"online": true,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "Biography of Test User1",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
|
||||
{ "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
|
||||
{ "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
|
||||
{ "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": true,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 1,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 10,
|
||||
"audio_latency": 5,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "12",
|
||||
"first_name": "Test",
|
||||
"last_name": "User12",
|
||||
"name": "Test User12",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "13",
|
||||
"first_name": "Test",
|
||||
"last_name": "User13",
|
||||
"name": "Test User13",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "14",
|
||||
"first_name": "Test",
|
||||
"last_name": "User14",
|
||||
"name": "Test User14",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"first_name": "Test",
|
||||
"last_name": "User15",
|
||||
"name": "Test User15",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
"first_name": "Test",
|
||||
"last_name": "User16",
|
||||
"name": "Test User16",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "17",
|
||||
"first_name": "Test",
|
||||
"last_name": "User17",
|
||||
"name": "Test User17",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "18",
|
||||
"first_name": "Test",
|
||||
"last_name": "User18",
|
||||
"name": "Test User18",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "19",
|
||||
"first_name": "Test",
|
||||
"last_name": "User19",
|
||||
"name": "Test User19",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "20",
|
||||
"first_name": "Test",
|
||||
"last_name": "User20",
|
||||
"name": "Test User20",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
}
|
||||
],
|
||||
"offset": 20,
|
||||
"my_audio_latency": 5,
|
||||
"filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",
|
||||
"description": "Current Search: Sort = Latency to Me",
|
||||
"is_blank_filter": false
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
{
|
||||
"musicians": [
|
||||
{
|
||||
"id": "21",
|
||||
"first_name": "Test",
|
||||
"last_name": "User11",
|
||||
"name": "Test User11",
|
||||
"city": "Denver",
|
||||
"state": "CO",
|
||||
"country": "US",
|
||||
"online": true,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "Biography of Test User1",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 },
|
||||
{ "instrument_id": "keyboard", "description": "Keyboard", "proficiency_level": 3, "priority": 8 },
|
||||
{ "instrument_id": "ukulele", "description": "Ukulele", "proficiency_level": 3, "priority": 11 },
|
||||
{ "instrument_id": "voice", "description": "Voice", "proficiency_level": 3, "priority": 13 },
|
||||
{ "instrument_id": "piano", "description": "Piano", "proficiency_level": 2, "priority": 10 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": true,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 1,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 10,
|
||||
"audio_latency": 5,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "22",
|
||||
"first_name": "Test",
|
||||
"last_name": "User12",
|
||||
"name": "Test User12",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "23",
|
||||
"first_name": "Test",
|
||||
"last_name": "User13",
|
||||
"name": "Test User13",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "24",
|
||||
"first_name": "Test",
|
||||
"last_name": "User14",
|
||||
"name": "Test User14",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "25",
|
||||
"first_name": "Test",
|
||||
"last_name": "User15",
|
||||
"name": "Test User15",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "26",
|
||||
"first_name": "Test",
|
||||
"last_name": "User16",
|
||||
"name": "Test User16",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "27",
|
||||
"first_name": "Test",
|
||||
"last_name": "User17",
|
||||
"name": "Test User17",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "28",
|
||||
"first_name": "Test",
|
||||
"last_name": "User18",
|
||||
"name": "Test User18",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "29",
|
||||
"first_name": "Test",
|
||||
"last_name": "User19",
|
||||
"name": "Test User19",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
},
|
||||
{
|
||||
"id": "30",
|
||||
"first_name": "Test",
|
||||
"last_name": "User20",
|
||||
"name": "Test User20",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"country": "US",
|
||||
"online": false,
|
||||
"musician": true,
|
||||
"photo_url": null,
|
||||
"biography": "",
|
||||
"full_score": null,
|
||||
"instruments": [
|
||||
{ "instrument_id": "acoustic guitar", "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1 }
|
||||
],
|
||||
"followings": [],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"pending_friend_request": false,
|
||||
"friend_count": 0,
|
||||
"follow_count": 0,
|
||||
"recording_count": 0,
|
||||
"session_count": 0,
|
||||
"audio_latency": null,
|
||||
"last_active_timestamp": 1629916641,
|
||||
"genres": []
|
||||
}
|
||||
],
|
||||
"offset": null,
|
||||
"my_audio_latency": 5,
|
||||
"filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",
|
||||
"description": "Current Search: Sort = Latency to Me",
|
||||
"is_blank_filter": false
|
||||
}
|
||||
|
|
@ -44,9 +44,12 @@
|
|||
"online_presences": [
|
||||
{ "id": "e1962204-f652-41b0-84d6-1afd7e9172be", "service_type": "soundcloud", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b0", "service_type": "reverbnation", "username": "testuser" },
|
||||
{ "id": "2dd22eef-03ba-4743-b65b-5a194591dc86", "service_type": "bandcamp", "username": "testuser" }
|
||||
{ "id": "2dd22eef-03ba-4743-b65b-5a194591dc86", "service_type": "bandcamp", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b1", "service_type": "fandalism", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b2", "service_type": "youtube", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b3", "service_type": "facebook", "username": "testuser" },
|
||||
{ "id": "005a7c78-db8b-4f72-a51f-d64d579c22b4", "service_type": "twitter", "username": "testuser" }
|
||||
],
|
||||
"performance_samples": [],
|
||||
"genres": [
|
||||
{ "genre_id": "classical", "player_type": "JamRuby::User", "genre_type": "profile" },
|
||||
{ "genre_id": "blues", "player_type": "JamRuby::User", "genre_type": "free_sessions" }
|
||||
|
|
@ -70,6 +73,16 @@
|
|||
{ "description": "Acoustic Guitar", "proficiency_level": 3, "priority": 1, "instrument_id": "acoustic guitar" },
|
||||
{ "description": "Keyboard", "proficiency_level": 3, "priority": 8, "instrument_id": "keyboard" }
|
||||
],
|
||||
"performance_samples": [
|
||||
{
|
||||
"id": "1",
|
||||
"service_type": "youtube",
|
||||
"claimed_recording": {
|
||||
"id": "1",
|
||||
"name": "Test Recording1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_friend": false,
|
||||
"is_following": false,
|
||||
"is_liking": false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
[
|
||||
{
|
||||
"id": "df953ba-7c59-4762-8cc3-279db82e872a",
|
||||
"name": "Open Session",
|
||||
"description": "Feel free to join this session, it's open!",
|
||||
"musician_access": true,
|
||||
"approval_required": false,
|
||||
"friends_can_join": false,
|
||||
"fan_access": true,
|
||||
"fan_chat": false,
|
||||
"user_id": "29becbf4-8be5-4078-9405-0edadc9fa42d",
|
||||
"claimed_recording_initiator_id": null,
|
||||
"track_changes_counter": 0,
|
||||
"max_score": 0,
|
||||
"backing_track_path": null,
|
||||
"metronome_active": false,
|
||||
"jam_track_initiator_id": null,
|
||||
"jam_track_id": null,
|
||||
"music_session_id_int": 2210,
|
||||
"use_video_conferencing_server": false,
|
||||
"music_notations": [],
|
||||
"participants": [
|
||||
{
|
||||
"ip_address": "192.168.1.110",
|
||||
"client_id": "63cdbcf7-a3c6-49bf-9412-0c09d4a6796b",
|
||||
"joined_session_at": "2023-10-26T07:16:22.605Z",
|
||||
"id": "3c1f2a74-0ccf-4ed1-9828-ce909edf61b7",
|
||||
"metronome_open": false,
|
||||
"is_jamblaster": false,
|
||||
"client_role": "parent",
|
||||
"parent_client_id": null,
|
||||
"client_id_int": 78125,
|
||||
"tracks": [
|
||||
{
|
||||
"id": "833de71f-7bc0-4d8e-9ea6-8a695181960b",
|
||||
"connection_id": "3c1f2a74-0ccf-4ed1-9828-ce909edf61b7",
|
||||
"instrument_id": "electric guitar",
|
||||
"sound": "stereo",
|
||||
"client_track_id": "FWAPMulti_2_10200m",
|
||||
"client_resource_id": "FWAPMulti_2_10200",
|
||||
"updated_at": "2023-10-26T07:16:22.611Z",
|
||||
"instrument": "Electric Guitar"
|
||||
}
|
||||
],
|
||||
"backing_tracks": [],
|
||||
"user": {
|
||||
"id": "29becbf4-8be5-4078-9405-0edadc9fa42d",
|
||||
"photo_url": null,
|
||||
"name": "Nuwan Chathuranga",
|
||||
"is_friend": true,
|
||||
"connection_state": "connected",
|
||||
"subscription": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"invitations": [
|
||||
{
|
||||
"id": "d49f3c07-7f49-4dad-8de1-2020046438de",
|
||||
"sender_id": "29becbf4-8be5-4078-9405-0edadc9fa42d",
|
||||
"receiver_id": "27bd4a30-d1b8-4eea-8454-01a104d59381"
|
||||
}
|
||||
],
|
||||
"lesson_session": null,
|
||||
"mount": {
|
||||
"id": "c3504b02-dc18-4d85-a562-117eaaffc136",
|
||||
"name": "/5tZz2_G8kT0-8UlUAna_yQ.mp3",
|
||||
"sourced": false,
|
||||
"listeners": 0,
|
||||
"bitrate": 128,
|
||||
"subtype": null,
|
||||
"url": "http://localhost:10000/5tZz2_G8kT0-8UlUAna_yQ.mp3",
|
||||
"mime_type": "audio/mpeg"
|
||||
},
|
||||
"can_join": true,
|
||||
"genres": [
|
||||
"Pop"
|
||||
],
|
||||
"recording": null,
|
||||
"share_url": "http://www.jamkazam.local:3000/s/T50BWPH9ICC",
|
||||
"session_controller_id": "29becbf4-8be5-4078-9405-0edadc9fa42d"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,373 +0,0 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
describe('Friends page without data', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate();
|
||||
cy.intercept('POST', /\S+\/filter/, {
|
||||
musicians: []
|
||||
});
|
||||
});
|
||||
|
||||
it('shows no records alert', () => {
|
||||
cy.visit('/friends');
|
||||
cy.contains('No Records!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Friends page with data', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: '2' }); //currentUser id is 2 - people.yaml fixture
|
||||
cy.intercept('POST', /\S+\/filter/, { fixture: 'people' }).as('getPeople');
|
||||
});
|
||||
|
||||
describe('listing users', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('lists musician users', () => {
|
||||
cy.contains('Find New Friends').should('exist');
|
||||
cy.contains('Update Search').should('exist');
|
||||
cy.contains('Reset Filters').should('exist');
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.should('have.length', 20)
|
||||
.first()
|
||||
.contains('Test User1');
|
||||
});
|
||||
|
||||
//TODO: paginate
|
||||
});
|
||||
|
||||
describe('user details side panel', () => {
|
||||
const showSidePanelContent = () => {
|
||||
cy.get('[data-testid=profileSidePanel] h4').should('have.text', 'Test User1');
|
||||
cy.get('[data-testid=profileSidePanel] .modal-body p').within(() => {
|
||||
cy.contains('Location: Denver, US')
|
||||
.and('contain', 'Location: Denver, US')
|
||||
.and('contain', 'Skill Level: Professional')
|
||||
.and('contain', 'Joined JamKazam: 08-26-2021')
|
||||
.and('contain', 'Last Active:')
|
||||
.and('contain', 'Latency to Me:');
|
||||
cy.get('.latency-badge').contains('UNKNOWN');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=profileSidePanel] .modal-body').within(() => {
|
||||
cy.get('[data-testid=biography]').contains('Biography of Test User1');
|
||||
|
||||
//instruments
|
||||
cy.get('[data-testid=instruments]').contains('Acoustic Guitar: Expert');
|
||||
cy.get('[data-testid=instruments]').contains('Keyboard: Expert');
|
||||
|
||||
//genres
|
||||
cy.get('[data-testid=genres]').contains('classical, blues');
|
||||
|
||||
//bands
|
||||
cy.get('[data-testid=bands]').contains('The Band');
|
||||
|
||||
//performance_samples
|
||||
//cy.get('[data-testid=performance_samples]').contains('The Band')
|
||||
|
||||
//online presence
|
||||
cy.get('[data-testid=online_presences]').contains('Soundcloud');
|
||||
cy.get('[data-testid=online_presences]').contains('Reverbnation');
|
||||
});
|
||||
};
|
||||
|
||||
const closeSidePanel = () => {
|
||||
cy.get('[data-testid=profileSidePanel] .modal-header button.close').click();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('shows profile side panel', () => {
|
||||
//open side panel by clicking name
|
||||
cy.contains('Test User1').click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
|
||||
//open side panel by clicking more button
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=btnMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
|
||||
//open side panel by clicking more link
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.first()
|
||||
.find('[data-testid=linkMore]')
|
||||
.click();
|
||||
showSidePanelContent();
|
||||
closeSidePanel();
|
||||
});
|
||||
});
|
||||
|
||||
describe('making friendship', () => {
|
||||
it('add friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.intercept('POST', /\S+\/friend_requests/, { statusCode: 201, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User2').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
cy.get('[data-testid=confirmFriendRequestModal]')
|
||||
.within(() => {
|
||||
cy.contains('Send a friend request to Test User2')
|
||||
cy.contains('Yes').click()
|
||||
})
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.disabled');
|
||||
cy.contains('Friend request was sent');
|
||||
});
|
||||
|
||||
it('remove friend', () => {
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'friend' });
|
||||
cy.intercept('DELETE', /\S+\/friends\S+/, { statusCode: 204, body: { ok: true } });
|
||||
|
||||
cy.visit('/friends');
|
||||
cy.contains('Test User1').click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('exist')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=disconnect]')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('[data-testid=profileSidePanel]')
|
||||
.find('[data-testid=connect]')
|
||||
.should('be.exist')
|
||||
.should('not.be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('chat window', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('is not disabled for friends', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.should('not.be.disabled')
|
||||
.trigger('mouseover');
|
||||
cy.contains('Send a message').should('exist');
|
||||
});
|
||||
|
||||
it('is disabled for non friends', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(1)
|
||||
.find('[data-testid=message]')
|
||||
.should('be.disabled');
|
||||
//cy.contains('You can message this user once you are friends').should('exist')
|
||||
});
|
||||
|
||||
it('lists text messages', () => {
|
||||
//initially show the most recent messages on openning chat window modal
|
||||
let numberOfMessages = 10;
|
||||
cy.fixture('text_messages_page1').then(json => {
|
||||
cy.intercept('GET', /\S+\/text_messages\S+/, json).as('getTextMessages');
|
||||
});
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.wait('@getTextMessages');
|
||||
cy.get('[data-testid=textMessageModal]')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
cy.get('.text-message-row').should('have.length', numberOfMessages);
|
||||
|
||||
//display previous messages by scrolling up
|
||||
const messageFixtures = ['text_messages_page2', 'text_messages_page3'];
|
||||
messageFixtures.forEach(fixture => {
|
||||
cy.fixture(fixture).then(json => {
|
||||
cy.intercept('GET', /\S+\/text_messages\S+/, json);
|
||||
cy.get('.modal-body .ScrollbarsCustom')
|
||||
.trigger('mouseover')
|
||||
.scrollTo('bottom');
|
||||
cy.get('.modal-body .ScrollbarsCustom')
|
||||
.trigger('mouseover')
|
||||
.scrollTo('top');
|
||||
numberOfMessages = numberOfMessages + 10;
|
||||
cy.get('.text-message-row').should('have.length', numberOfMessages);
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('button')
|
||||
.contains('Close')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
});
|
||||
cy.get('[data-testid=textMessageModal]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('sends message by clicking send button', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
cy.get('textarea').type('Hello');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
cy.get('textarea').should('have.value', '');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('sends message by pressing enter key', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
cy.get('textarea').type('Hello{enter}');
|
||||
cy.get('textarea').should('have.value', '');
|
||||
cy.get('button')
|
||||
.contains('Send')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('goes away by clicking close button', () => {
|
||||
cy.get('[data-testid=peopleListTable] > tbody tr')
|
||||
.eq(0)
|
||||
.find('[data-testid=message]')
|
||||
.click();
|
||||
cy.get('[data-testid=textMessageModal]').within(() => {
|
||||
cy.get('button')
|
||||
.contains('Close')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
});
|
||||
cy.get('[data-testid=textMessageModal]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it.skip('shows received message by other user', () => {
|
||||
//TODO: this should be test in e2e test
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter', () => {
|
||||
const fillFilterForm = () => {
|
||||
//cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_fair]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_high]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_beginner]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_intermediate]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_expert]').uncheck();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=proficiency_expert]').uncheck();
|
||||
cy.get('#selInstruments')
|
||||
.type('Drums{enter}')
|
||||
.click();
|
||||
cy.get('#selGenres')
|
||||
.type('Pop{enter}')
|
||||
.click();
|
||||
cy.get('#selLastActive').type('Within last 1 Day{enter}');
|
||||
cy.get('#selJoinedWithin').type('Within last 7 Day{enter}');
|
||||
}
|
||||
beforeEach(() => {
|
||||
cy.visit('/friends');
|
||||
});
|
||||
|
||||
it('open and close filter modal', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').should('be.visible');
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=modalUpdateSearch]').should('not.be.visible');
|
||||
});
|
||||
|
||||
it('reset filters', () => {
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
fillFilterForm()
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').should('not.be.checked')
|
||||
cy.get('[data-testid=modalUpdateSearch]')
|
||||
.contains('Cancel')
|
||||
.click();
|
||||
cy.get('[data-testid=btnResetSearch]').click() //click reset button
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=modalUpdateSearch] input[name=latency_good]').should('be.checked')
|
||||
})
|
||||
|
||||
it('submit filter form with params', () => {
|
||||
//wait for stubbed request sent to fetch data for initial page load
|
||||
cy.wait('@getPeople').then(interception => {
|
||||
assert.isNotNull(interception.response.body, '1st API call has data');
|
||||
});
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
//wait for stubbed request sent by submitting search form without filling any form field
|
||||
cy.wait('@getPeople')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '2nd API call has data');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.equal', {
|
||||
latency_good: true,
|
||||
latency_fair: true,
|
||||
latency_high: true,
|
||||
proficiency_beginner: true,
|
||||
proficiency_intermediate: true,
|
||||
proficiency_expert: true,
|
||||
instruments: [],
|
||||
genres: []
|
||||
});
|
||||
|
||||
cy.get('[data-testid=btnUpdateSearch]').click();
|
||||
fillFilterForm()
|
||||
cy.get('[data-testid=btnSubmitSearch]').click();
|
||||
|
||||
//wait for stubbed request sent by submitting search form again. but this time fill form fields
|
||||
cy.wait('@getPeople')
|
||||
.then(interception => {
|
||||
assert.isNotNull(interception.response.body, '3rd API call has data');
|
||||
})
|
||||
.its('request.body')
|
||||
.should('deep.equal', {
|
||||
latency_good: false,
|
||||
latency_fair: false,
|
||||
latency_high: false,
|
||||
proficiency_beginner: false,
|
||||
proficiency_intermediate: false,
|
||||
proficiency_expert: false,
|
||||
instruments: [{ value: 'drums', label: 'Drums' }],
|
||||
genres: ['pop'],
|
||||
active_within_days: '1',
|
||||
joined_within_days: '7'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
describe("Top Navigation", () => {
|
||||
|
||||
const showSubscribeToUpdates = () => {
|
||||
cy.contains('Keep JamKazam Improving').should('exist')
|
||||
cy.contains('Subscribe').should('exist')
|
||||
}
|
||||
|
||||
const showProfileDropdown = () => {
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('exist')
|
||||
cy.contains("Peter Pan").should('exist')
|
||||
//cy.contains("My Profile").should('exist')
|
||||
cy.contains("Sign out").should('exist')
|
||||
}
|
||||
|
||||
describe("when user has not logged in", () => {
|
||||
beforeEach(() => {
|
||||
cy.stubUnauthenticate()
|
||||
cy.visit('/')
|
||||
});
|
||||
|
||||
it("does not show user dropdown", () => {
|
||||
cy.contains("Sign in to begin")
|
||||
cy.get('a.btn').should('have.text', 'Sign in')
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('not.exist')
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe("when user has logged in", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate()
|
||||
cy.visit('/')
|
||||
});
|
||||
|
||||
it("shows user dropdown", () => {
|
||||
showSubscribeToUpdates()
|
||||
showProfileDropdown()
|
||||
})
|
||||
|
||||
it('sign out', () => {
|
||||
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').contains('Peter Pan').trigger('mouseover')
|
||||
cy.stubUnauthenticate()
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').contains('Sign out').click()
|
||||
cy.get('[data-testid=navbarTopProfileDropdown]').should('not.exist')
|
||||
cy.contains("Sign in to begin")
|
||||
})
|
||||
})
|
||||
|
||||
describe('header notifications', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate()
|
||||
cy.intercept('GET', /\S+\/notifications/, { fixture: 'notifications'} )
|
||||
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
|
||||
cy.visit('/')
|
||||
})
|
||||
|
||||
it('shows notifications', () => {
|
||||
cy.get('[data-testid=notificationDropdown]').should('not.be.visible')
|
||||
cy.get('.notification-indicator').trigger('mouseover')
|
||||
cy.get('[data-testid=notificationDropdown]').should('be.visible')
|
||||
cy.get('[data-testid=notificationDropdown] .list-group-item').should('have.length', 3)
|
||||
cy.get('[data-testid=notificationDropdown]').contains('View all').click() //view all notifications
|
||||
cy.url().should('include', '/notifications')
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 253 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue