Compare commits

..

279 Commits

Author SHA1 Message Date
Seth Call 5a68b57a76 track utm_id 2026-02-04 21:53:36 -06:00
Seth Call 577bece75e Add threads to sources 2026-02-04 19:53:53 -06:00
Seth Call 3ee8577153 Fix Event 2026-01-28 20:03:52 -06:00
Seth Call 8aa5ba743e add array support for facebook ad source 2026-01-24 00:01:40 -06:00
Seth Call 3a4b900ebd Support more utm tracking 2026-01-23 23:31:34 -06:00
Seth Call bfbd266466 build this 2026-01-22 22:36:58 -06:00
Seth Call b6fc597f8b fix reporting 2026-01-22 22:18:11 -06:00
Seth Call 9761d8f44d removing rails loger junk 2026-01-19 13:23:33 -06:00
Seth Call 85d2b3fd53 Store / read UTM 2026-01-17 20:57:36 -06:00
Seth Call 0113408780 Add meta tracking to jam-ui 2026-01-16 07:56:26 -06:00
Seth Call a283c39a58 more logging for cAPI event 2026-01-15 08:01:59 -06:00
Seth Call f3d8c4763b Add beter logging 2026-01-15 07:52:05 -06:00
Seth Call e92e54fcd3 Missed some recurly/plan code for yearly 2026-01-14 22:12:58 -06:00
Seth Call 0fd37809c8 add all this stuff in 2026-01-14 21:04:58 -06:00
Nuwan caf3e2b2d5 Update "Zoom weekly office hours" link in Welcome to JamKazam email 2025-09-03 13:58:44 +05:30
Seth Call 6d2211cbb4 VRFS-5627 - VUs get busted when hovering over remote participants who change tracks/leave/change track settings 2025-08-03 11:51:12 -05:00
Seth Call f755e2b0a0 VRFS-5653 - fix Music/Chat slider 2025-08-02 11:04:46 -05:00
Seth Call 80010723ca Force build 2025-07-26 08:34:09 -05:00
Nuwan ef2497d0a4 remove audio delay from recording interface
remove audioDelay input and related code. also removed
the volume control which was on the same interface
2025-07-17 17:15:33 +05:30
Nuwan e6aad11685 remove unused video formats in recording window
remove .mkv, .ts, .flv, and .m3u8 options from the
video format list box, leaving only .mp4 and .mov options
2025-07-03 20:26:46 +05:30
Nuwan Chaturanga db2010e893 Merged in 5628-vu-meter_frequency (pull request #62)
Implement VU meter frequency preference

* Merged promised_based_api_interation into 5628-vu-meter_frequency

* Merge branch '5628-vu-meter_frequency' of bitbucket.org:jamkazam/jam-cloud into 5628-vu-meter_frequency

* force push

* wip

* wip VU meter update pref rendering

* fix for vu meter update

this fixes the previously broken vu meter update based on the user
selected update rate preference.

* Merge branch '5628-vu-meter_frequency' of bitbucket.org:jamkazam/jam-cloud into 5628-vu-meter_frequency


Approved-by: Seth Call
2025-06-20 10:48:47 +00:00
Seth Call 14dbef2be1 Force a build 2025-06-16 11:48:51 -05:00
Seth Call 915fa31f09 Indicate this comes from the 'modern' client 2025-06-15 14:23:50 -05:00
Seth Call c874158bd7 Fix diagnostic for GEAR_SELECTION 2025-06-15 14:22:30 -05:00
Nuwan 369dd10a02 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2025-06-15 04:02:58 +05:30
Nuwan 41fb6e7a2a force push 2025-06-15 04:02:17 +05:30
Seth Call 6e478d3411 Deactivate web/video setting for now 2025-06-15 04:02:17 +05:30
Nuwan 5ff7b049ce remove noisy log entry 2025-06-15 03:39:08 +05:30
Nuwan 82bc1c408b Implement VU meter frequency preference
render VU meter updates as per the user preference
2025-06-15 01:19:17 +05:30
Seth Call e82fea8d3d Deactivate web/video setting for now 2025-06-13 07:28:29 -05:00
Seth Call 324d34ff61 Recording seems to be working well enough atm. With new clients only 2025-06-08 22:24:37 -05:00
Seth Call e83805bd4a pause 2025-06-08 14:34:23 -05:00
Seth Call 02666d1680 Working on so far. at coffee shop pause 2025-06-08 14:34:10 -05:00
Seth Call d573904d05 pause before change to js 2025-06-07 12:15:08 -05:00
Seth Call cb24078c19 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2025-05-28 16:38:53 -05:00
Seth Call 4931908344 Update recording feature to show recording in progresso for late joiners. also update start recording button 2025-05-28 16:38:41 -05:00
Nuwan Chaturanga 2983b801f6 Merged in 5538-modal_dialog_to_remind_gear_setup (pull request #60)
5538 modal dialog to remind gear setup

* Setup Gear prompt

Display each time user runs app if Setup Gear step in PLG funnel not yet
completed


Approved-by: Seth Call
2025-05-23 13:01:45 +00:00
Nuwan 194c6156e0 chage references of beta.jamkazam to www.jamkazam 2025-05-21 12:17:06 +05:30
Nuwan 8ae89c1cbb debug custom URL handling in app 2025-04-30 17:40:21 +05:30
Nuwan a17c29216c 5551 2025-04-28 23:35:41 +05:30
Nuwan f88def1790 fixes related to recording state 2025-04-23 11:43:29 +05:30
Nuwan f666578872 add debug log to check recording video state 2025-04-21 15:19:57 +05:30
Nuwan 911a37bec5 fixing state issues of front end recording 2025-04-18 18:15:30 +05:30
Nuwan fdb392f723 wip 2025-04-17 00:35:54 +05:30
Nuwan ce5bb82fb5 wip 2025-04-16 23:47:24 +05:30
Nuwan 0f074b1e37 wip 2025-04-16 23:32:06 +05:30
Nuwan bbf4688830 wip 2025-04-16 23:25:49 +05:30
Nuwan e1b565847c wip 2025-04-16 19:31:34 +05:30
Nuwan c28508bcf7 recording state fix wip 2025-04-10 00:35:26 +05:30
Nuwan fd64e946bf FrontStopRecording api added 2025-04-04 04:46:21 +05:30
Nuwan 844633397f fixes for session recording with other participants 2025-04-04 03:19:00 +05:30
Nuwan ab34c37d80 changes to allow joining a seesion without stopping an onging recording 2025-04-02 18:50:37 +05:30
Nuwan 5d9a75deb3 wip chages in session recording 2025-03-31 13:22:57 +05:30
Nuwan c277fdce94 wip 2025-03-27 18:53:45 +05:30
Nuwan 76aefd6227 wip on leaving a session while recording 2025-03-17 15:51:59 +05:30
Nuwan eb9ad97ada validate custom URLs before processing 2025-02-08 18:34:53 +05:30
Nuwan c82a01361f customUrl validation 2025-02-06 22:11:01 +05:30
Nuwan e2828b0387 notify other participants on session leave
on leaving the session directly delete the participant record and
notify other clients ref: VRFS-5529
2025-02-05 01:04:11 +05:30
Nuwan 254ad61168 recording dialog update
add Audio Files options to select file storage options
add help help bubbles to audio files, audio delay and voice chat
2025-02-04 17:33:41 +05:30
Nuwan 532f29f3db changes to customUrl handling in client 2025-01-30 13:28:15 +05:30
Nuwan 9abaa539db fixes to app loading by custom URLs 2025-01-29 09:50:50 +05:30
Nuwan 0ed89f4e38 customUrl handling fixes 2025-01-28 17:18:58 +05:30
Nuwan 867b159c63 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2025-01-18 15:27:28 +05:30
Nuwan ccf3b3b5e7 force user to go to new jamtracks page (beta) once user clicks the JamTracks tile in client home page 2025-01-18 15:26:36 +05:30
Seth Call 767e1c7c83 Just how to test the start command 2024-12-14 14:32:12 -06:00
Seth Call fa068ad8be force build 2024-12-01 21:39:53 -06:00
Nuwan 1c47c70d35 custom url debug 2024-11-30 11:12:45 +05:30
Nuwan 80f0fa8bd9 add debug entries to check custom url 2024-11-23 20:00:26 +05:30
Nuwan ea3cecbfcf debug customUrl 2024-11-23 10:23:40 +05:30
Nuwan 9f59074b0e custom URL handling on onLoggedIn 2024-11-23 06:29:28 +05:30
Nuwan ba6c3c56dc joinSession custom URL: change parameter name 2024-11-22 22:59:07 +05:30
Nuwan e262c601b7 custom URL related adjestments 2024-11-17 00:10:20 +05:30
Nuwan f41c64acc8 comment duplicate code 2024-11-16 08:51:29 +05:30
Nuwan 8194e4c636 join session using custom url scheme 2024-11-15 22:18:58 +05:30
Nuwan 8dc3a56870 tweeks to session create from custom URL scheme 2024-11-15 18:06:41 +05:30
Nuwan 0bd3f9463d fixes for creating sessions by custom URL 2024-10-28 09:08:22 +05:30
Nuwan fd8f31e7d4 custom URL schema handling 2024-10-25 17:08:28 +05:30
Nuwan a5fce73848 wip - custom url handling 2024-10-25 08:29:13 +05:30
Nuwan 2b929c0d1e add debug log entries to identify possible errors when creating a session using custom URL 2024-10-23 23:54:13 +05:30
Nuwan 489f3a685d add debug log entries to identify possible errors when creating a session using custom URL 2024-10-23 23:42:48 +05:30
Nuwan cef7f1efbe custom URL schema for create a session
handles URL loaded as a custom URL format (jamkazam://)
which is meant to create a new session with the provided
parameters
2024-10-23 11:33:57 +05:30
Nuwan de115773ec add more logging to debug create session from custom URL 2024-10-18 08:34:35 +05:30
Nuwan 7f693b7e54 bug fixing in custom url app launching 2024-10-17 14:31:49 +05:30
Nuwan 12feacc908 add logging for debugging 2024-10-17 13:49:22 +05:30
Nuwan a718037dd3 customUrl handle - launch jk session screen when is invoked using custom URL 2024-10-17 00:12:22 +05:30
Nuwan c9528feabf assign session if as newSessionId to identify when the session is created 2024-10-16 13:52:56 +05:30
Nuwan 505d7c0496 invoking the client to create a session using custom URL 2024-10-16 12:49:56 +05:30
Nuwan 15583ce99d onLogginIn api handle for custom url wip 2024-10-16 12:49:56 +05:30
Nuwan 1280a3ca86 wip custom url handling 2024-10-16 12:49:56 +05:30
Seth Call 243a081aa9 Make the previously-slow query target feed query configurable. Default to on 2024-10-14 11:19:19 -05:00
Nuwan f71c0913b7 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2024-10-11 22:05:14 +05:30
Seth Call 1ae5437618 Fix recurly bug where updated 2x from a RJS token 'ruins' the account until the user re-deploys 2024-10-11 22:00:38 +05:30
Nuwan fb80a3a2c5 join session by custom url schema 2024-10-11 22:00:10 +05:30
Nuwan c9a3f7d1da Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2024-10-11 08:54:15 +05:30
Nuwan 98273bae0d handle session invocation by custom URL schema 2024-10-11 08:50:10 +05:30
Seth Call a3e0666202 Fix recurly bug where updated 2x from a RJS token 'ruins' the account until the user re-deploys 2024-10-05 15:55:37 -05:00
Nuwan 317c0384e1 add await on SetScoreWorkTimingInterval 2024-09-26 08:29:49 +05:30
Nuwan 2546d3f550 change order of LaunchBroadcastSettings enum item in asyncJamClient 2024-09-25 22:22:23 +05:30
Nuwan 57e669cd22 session setting fix musician access drop down always disabled 2024-09-21 11:00:28 +05:30
Seth Call 1e50060087 Backport JAM_REASON_PRESENT into user model, to co-exist happily with develop servers 2024-09-17 22:27:27 -05:00
Nuwan 08fe7808a0 remove video gear link from account home page 2024-09-13 08:18:55 +05:30
Nuwan 7ea8a1e8fa Session broadcast button
add broadcast button which send a message to the client
2024-08-21 22:41:23 +05:30
Seth Call 279ba30506 Force build 2024-08-12 12:50:48 -05:00
Nuwan 2a64bbbfe8 fix js reference (this) error in waitForSessionPageEnterDone function 2024-04-11 17:03:28 +05:30
Nuwan 7617e4a4dd use jquery $.deferred in waitForSessionPageEnterDone
change the ES6 promise way of handling this method.
actually some of the code in this file expects $.deferred object
to be presented.
2024-04-11 14:54:48 +05:30
Nuwan 8bb0fa2809 fix not able to delete band 2024-03-09 16:34:21 +05:30
Nuwan 7c2ffe01ef fix band section listing musicians 2024-03-03 17:32:59 +05:30
Nuwan bcd819dfc3 fix recording window glitches
use RecordingStore instead of AppStore to track audioFormat changes.
this prevents session related state being altered unexpectedly.
2023-11-17 16:00:03 +05:30
Nuwan 8d4ed14fd6 sync recording audio format change with back end audio recording menu 2023-11-15 18:46:51 +05:30
Nuwan 03bb4190f1 sync audio recording format
on session startup fetch the audio format selected in the back end
and assign it in the front end audio recording window.
2023-11-14 17:37:19 +05:30
Nuwan 4a30d29c4b fix recording stop
this fix addresses the issue when stopping a recording in a subsequent
attemp to record
2023-10-17 18:09:12 +05:30
Nuwan b185a60656 change enum order of asyncJamClient 2023-10-12 23:08:19 +05:30
Nuwan 2b1309a3b0 change recording allowed video formats 2023-10-04 02:01:01 +05:30
Nuwan c116559d18 clean up comment 2023-10-04 01:17:09 +05:30
Nuwan 0617b1ba8d change in audio formats list for recording 2023-10-04 01:13:45 +05:30
Nuwan 001f59ba30 new session recording feature
for video recordings check if OBS has been installed on the
user's computer.
2023-10-03 19:17:26 +05:30
Nuwan 25586e06fc changes to allowed audio formats in new recording window 2023-10-03 01:16:09 +05:30
Nuwan 5618b08e79 call on start recording the new StartMediaRecording API 2023-09-30 10:07:08 +05:30
Nuwan e9ff7c5faa add IsOBSAvailable api in to asyncJamClient 2023-09-30 00:18:27 +05:30
Nuwan 6acf990b20 add StartMediaRecording api in to asyncJamClient 2023-09-30 00:08:09 +05:30
Nuwan da762dcd2c Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-09-29 23:47:16 +05:30
Nuwan e1ff954709 guard session recording 2023-09-29 23:46:42 +05:30
Nuwan c9d7a560d0 debug jamtrack volume 2023-09-29 23:46:42 +05:30
Nuwan 8ef9530129 guard session recording 2023-09-22 16:18:02 +05:30
Nuwan 40282a82f3 debug jamtrack volume 2023-09-21 17:16:08 +05:30
Nuwan 4a2046aae6 handle new recording window events 2023-09-21 10:45:53 +05:30
Nuwan ad9f5dcef3 wip on new session recording window 2023-09-19 13:13:44 +05:30
Nuwan 2dceeb86c3 wip new recording window 2023-09-19 13:13:44 +05:30
Nuwan 5727780259 merge into promised_based_api_interation 2023-09-19 13:12:34 +05:30
Nuwan 2093c6da9e wip new recording window 2023-09-19 13:11:55 +05:30
Nuwan 2adf75eb7e wip new recording 2023-09-19 13:11:55 +05:30
Nuwan a01a10afab WIP recording settings form 2023-09-19 13:11:55 +05:30
Nuwan 3eac4cc4b1 WIP in new session recording window. UI is almost done. need to add functinality 2023-09-19 13:11:55 +05:30
Nuwan 25d651f450 add more volume data to SessionSetTrackVolumeData
id, _id and groupID added to the third parameter of this API call
2023-09-19 11:56:39 +05:30
Nuwan f835894620 add more context details to SessionSetTrackVolumeData
add  mediaType, isJamTrack, and isMetronome to trackVolumeObject parameter
of SessionSetTrackVolumeData api
2023-09-13 18:29:35 +05:30
Nuwan bf1291abf4 removing unused bits 2023-09-07 20:03:34 +05:30
Nuwan 55d3202c11 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-09-05 19:22:16 +05:30
Nuwan 61b58eddd1 prevent showing alert on audio instrument select
fix this bug due to previous introduction of alert which pops up
if midi instrument is selected without selecting midi interface and
vst plugin. The alert only applicable to the midi interface.
2023-09-05 19:19:16 +05:30
Seth Call fc9d69f5f9 Update to pass product from client back to ArtifactUpdate 2023-09-02 15:23:22 -05:00
Seth Call 21733110f4 skip all client updatse 2023-09-02 14:53:19 -05:00
Seth Call f9c3c33d6b Beta download page 2023-09-01 16:33:09 -05:00
Nuwan ccee1c6805 cleanup debug entries 2023-09-01 16:54:01 +05:30
Nuwan f92ae4dd8d debugging 2023-09-01 15:57:35 +05:30
Nuwan 9e2adc9ce0 fix artifact version upgrade 2023-09-01 13:51:16 +05:30
Nuwan c8be3b847f debug JamServer 2023-09-01 13:33:09 +05:30
Nuwan 6d2ef50a8d debug version upgrade 2023-09-01 10:20:46 +05:30
Nuwan 63ebf8259c changes related to client upgrade fixes 2023-09-01 00:45:44 +05:30
Nuwan ea5e32e14b fix recording window handling on conneted clients to the session 2023-08-31 22:22:30 +05:30
Nuwan 7936d8ac59 debug 2023-08-31 09:47:46 +05:30
Nuwan fff48bf399 client update debug logs 2023-08-31 09:36:25 +05:30
Nuwan d0966fe67a debug backing track on peer 2023-08-28 18:14:45 +05:30
Nuwan 4f1fd4880e fix inconsistency of volume gauge
this fixes the unpredictable behavior of volume level when changing the
volume gauge leveler.
2023-08-22 23:09:44 +05:30
Nuwan e3cb68dda8 Show instructions in midi track configuration popup 2023-08-21 16:45:12 +05:30
Nuwan d8d04dd33f Allow multiple recordings within session
change in recording flow. Now after stopping the recording we no longer ask
user to save or discard the recording, instead the app saves the recording to user's
computer and opens the file explorer to that folder.
therefore following scenario is no longer valid; hence skipping it.
2023-08-19 11:03:29 +05:30
Nuwan 701620089a async/await fix 2023-08-11 03:24:11 +05:30
Nuwan 126e08a90d fix for version upgrade alert 2023-08-05 18:07:27 +05:30
Nuwan 48b2316728 debug version upgrade 2023-08-05 17:19:11 +05:30
Nuwan 03e12da98a audio MIDI config alert
show an alert when midi interface or midi plugin is not selected
2023-08-05 00:39:24 +05:30
Nuwan c7822a14d0 more cleaning console logs 2023-08-02 09:15:09 +05:30
Nuwan 758e688db1 remove debug entries and cleanup 2023-08-02 08:57:59 +05:30
Nuwan 5e7d512a5e fix for midi instrument selection 2023-08-02 08:32:11 +05:30
Nuwan 6d4b775321 debugging 2023-07-28 15:52:36 +05:30
Nuwan 2f866e92a7 debugging 2023-07-28 15:19:59 +05:30
Nuwan fd929ab0dc remove debug lines 2023-07-28 12:29:59 +05:30
Nuwan 4fbe7fb8a3 debug websocket gateway handle login 2023-07-28 07:59:03 +05:30
Nuwan 7390355b3c provide detailed os name in jamserver to be consumed by websocket gateway router 2023-07-28 07:53:51 +05:30
Nuwan 9329e5d235 debug logs 2023-07-22 09:50:15 +05:30
Nuwan 5ca37d63c1 debug versioncheck api 2023-07-22 04:32:55 +05:30
Nuwan 20331c6d60 fix for version upgrade 2023-07-22 03:59:49 +05:30
Nuwan 60009208c0 fix for audio input port assignment
this fix addresses the invalid input port assignment issue
due to asynchronous nature of asyncjamClient method calls
2023-07-21 18:32:40 +05:30
Nuwan 81b3fecfd4 fix for client version upgrade 2023-07-20 04:11:44 +05:30
Nuwan e03389909e fix showing error alert on audio resync 2023-07-20 03:31:37 +05:30
Nuwan 483eb20c88 ArtifactsUpdate MacOSX-M 2023-07-12 10:39:57 +05:30
Nuwan e764047935 change in userAgent check to determine client type 2023-07-11 22:30:48 +05:30
Nuwan 287fad8443 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-07-07 18:54:10 +05:30
Nuwan eba15f255e remove alert on session recording stop 2023-07-07 18:53:19 +05:30
Nuwan 42ee0bcf26 WIP fixing midi configuration 2023-07-07 18:53:19 +05:30
Nuwan 5932cc3d67 WIP midi and audio channel config 2023-07-07 18:53:19 +05:30
Nuwan b6868a4fbb Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-07-07 17:50:45 +05:30
Nuwan f1008baea8 remove alert on session recording stop 2023-07-07 17:50:12 +05:30
Nuwan 8e0e43359c WIP fixing midi configuration 2023-07-07 17:49:55 +05:30
Nuwan bc71730df4 remove alert on session recording stop 2023-07-06 10:05:21 +05:30
Nuwan 69addda196 fix Server Disconnect
add FakeJamClientProxy to wrap FakeJamClient. It accepts API calls and
sends back javascript promises.
2023-07-06 09:00:49 +05:30
Nuwan 917d8f1a8e WIP midi and audio channel config 2023-07-04 12:56:18 +05:30
Nuwan dd4f62cb07 fix js error 2023-06-14 16:55:13 +05:30
Nuwan 5d09d23809 suppress the preview recording window 2023-06-14 16:22:56 +05:30
Nuwan 01fda6b6f0 fix onSessionEnter 2023-06-02 06:48:56 +05:30
Nuwan d09baf05bb prevent session joining using web browser 2023-05-29 19:04:46 +05:30
Nuwan 03bb0f99fa fix start/stop recording dialog for session with video 2023-05-20 17:50:32 +05:30
Nuwan f4aebb44e2 fix start/stop recording dialog for session with video 2023-05-20 17:36:43 +05:30
Nuwan 7c306a13df remove debug message 2023-04-18 04:52:36 +05:30
Nuwan 6498485548 in session recording dialog box remove the video opts
with the newly invented webRTC based video recording system we can drop this options.
Only enable the video recording radio button if the backend has sent the relevant event.
2023-04-18 04:42:40 +05:30
Nuwan 0beb922db5 disable video gear setup link 2023-04-07 11:58:49 +05:30
Nuwan b8d8ca73fa sort user friend list in alphabetical order 2023-04-06 12:06:25 +05:30
Nuwan 8d816d69ed fix array iteration key/value in gear_util getChatInputs 2023-04-03 23:35:26 +05:30
Nuwan bb1c15205c fix GetDetailedOS 2023-03-18 20:09:09 +05:30
Nuwan 599e650d57 side bar search fixing
1) change the search results limit to 40
2) fix band search sql
2023-03-17 10:15:34 +05:30
Nuwan 76c56612cf fix showing build upgrade message on app startup 2023-03-16 12:26:29 +05:30
Nuwan 8d2e9d6663 fix async handler in updateSingleRecording 2023-03-14 20:26:45 +05:30
Nuwan 51f5f5ecf0 fix recording manager - not showing data 2023-03-14 13:38:55 +05:30
Nuwan dd0e24fa19 disable legacy video system 2023-02-24 23:23:10 +05:30
Nuwan aa3ac642da this includes fix for Resync button doesn't do anything 2023-02-24 17:23:19 +05:30
Nuwan 359ccfe431 fix async call order in configure audio
This error was appearing in audio gear configuration when clicking Next button in
select & test audio gear step. on this button click the program executed
an event listener on input channel check box click (programmatically) and
autoAssignToSingleInput inside the click event handler was invoked
an async call stack which was resolved before the next button click handler completed.
As a result a pop up was showing up saying "The application is no longer modifying a new profile".

This commits fixes this issue by explicitly preventing this event lister handler been executed
in this case.
2023-02-23 19:12:16 +05:30
Nuwan 4cef5acde8 new boolean parameter for TrackDeleteProfile to delete it from the Audio.ini file 2023-02-22 12:36:56 +05:30
Nuwan 172f3a654d new boolean parameter for TrackDeleteProfile to delete it from the Audio.ini file 2023-02-22 12:24:11 +05:30
Nuwan 972f9fadcc new boolean parameter for TrackDeleteProfile to delete it from the Audio.ini file 2023-02-22 11:28:37 +05:30
Nuwan 1fb6907df0 using Jquery .attr() for accessing data attributes. using .prop() here is erronious here 2023-02-22 00:16:42 +05:30
Nuwan 27d697149c fix js error when selecting framesize or sample rate settings in configure audio 2023-02-21 19:24:13 +05:30
Nuwan 60af5432ce fix frontend error when changing framesize in audio input/output configuration 2023-02-17 16:37:56 +05:30
Nuwan 19050d317b Fix configure voice chat
add missing async/await call to fetch chat inputs
2023-02-16 18:21:11 +05:30
Nuwan 80bf8119af show logs in asyncJamClient 2023-02-06 00:28:53 +05:30
Nuwan 3d26f241b4 turn off log messages in asyncJamClient 2023-02-04 03:48:19 +05:30
Nuwan b7f9ade2ea handle sendP2PMessage in asyncJamClient 2023-02-04 03:35:07 +05:30
Nuwan 4423a9c122 more async fixes related to audio interface configuration 2023-02-01 15:34:15 +05:30
Nuwan 6c56fe3af8 async calls in configure audio
adds async/await call for jamClient api calls
2023-01-31 17:58:16 +05:30
Nuwan 009ee6a6dd add GetDetailedOS in to async call array 2023-01-26 01:11:21 +05:30
Nuwan 20177f71dd Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-01-26 00:48:27 +05:30
Seth Call 37533c9e18 Fix odd bug in jam_track_right for current_user, and send https redirectfor download link for jamtrack 2023-01-16 13:08:40 -05:00
Nuwan 2228b91285 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-01-13 11:12:09 +05:30
Seth Call dccc668d48 Fix special branch casing 2023-01-04 17:57:27 -06:00
Seth Call 16c5dbb218 Fix special branch casing 2023-01-04 17:20:21 -06:00
Nuwan 46f2fbb55f Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2023-01-04 21:42:42 +05:30
Seth Call b3d4259b09 Try removing gemfile.lock 2023-01-04 08:44:02 -06:00
Seth Call d59b83bb42 wip 2023-01-04 08:43:13 -06:00
Seth Call 7dc640fb86 debugging build 2023-01-04 08:26:39 -06:00
Seth Call cbf348564d Try to fix build 2023-01-03 14:38:20 -06:00
Seth Call e9612cbc19 special case debians for this branch 2023-01-03 08:43:48 -06:00
Nuwan 5145bfeac1 add .ruby-version to .gitignore 2022-12-23 08:18:00 +05:30
Nuwan 3349c76076 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2022-12-16 13:51:08 +05:30
Murali Gowrisankaran bb9813ae26 Removed enumeration for open and close Url APIs 2022-12-16 02:37:30 +00:00
Nuwan bd1134b2bd Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2022-12-13 12:09:37 +05:30
Murali Gowrisankaran cc4d1575c0 Added enumerated constants for window popup APIs 2022-12-13 06:07:29 +00:00
Nuwan 033d67639c fix js error 2022-12-07 19:44:45 +05:30
Nuwan d9b4a5b8d9 fix async call issue in bands search window 2022-12-03 02:21:47 +05:30
Nuwan 87762c5933 add bugsnag error reporting to asyncJamClient 2022-11-05 04:36:05 +05:30
Nuwan 9d787351fa fixes for recording related errors 2022-11-04 18:57:09 +05:30
Nuwan f392623049 wip session recording related fixes 2022-11-03 18:54:03 +05:30
Nuwan c207b76641 debug and fixing session recording wip 2022-11-02 04:15:22 +05:30
Nuwan 2c5708b28a fix syntax error - invalid comment 2022-10-29 00:04:50 +05:30
Nuwan 2370762c51 alperbatically order JKFrontendMethods method names in asyncJamClient 2022-10-28 22:18:29 +05:30
Nuwan 3be0e25bcb add jamClient.SessionSetTrackVolumeData
this new api method is used to send session track data
over web channel to qt c++ backend
2022-10-28 22:06:34 +05:30
Nuwan 768f3d976a convert anoter few jamClient. call to async format 2022-10-28 13:12:14 +05:30
Nuwan 5487e62f5a fixes for public session creation and joining 2022-10-12 19:39:09 +05:30
Nuwan 38baea686f fixes for the errors - public session
fix errors when creating group session
and when joining group session
2022-10-07 04:16:40 +05:30
Seth Call 8c472a90a8 Fix jamtracks for local dev 2022-09-12 17:04:48 -05:00
Nuwan 73a56b2cf6 fix session metronome related functions 2022-09-07 08:49:21 +05:30
Nuwan 6486761483 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2022-09-02 11:11:11 +05:30
Nuwan 7a7986e30a fixing audio config and session related issues 2022-09-02 11:10:51 +05:30
Seth Call 1f63aa77b4 Pass in client_id to backend websocket connection 2022-09-01 16:44:16 -05:00
Seth Call cbbfebddc9 make onMixersChanged async so that it can await internally 2022-08-17 21:57:54 -05:00
Nuwan 794d131a3e change context._.each( loops to classic for loops 2022-08-15 22:30:58 +05:30
Seth Call 98aa2adc42 Remove 2 context._.each statements combined with async 2022-08-14 17:00:38 -05:00
Nuwan 21af8446ca async call fixes for audio gear setup 2022-08-13 00:33:24 +05:30
Nuwan 8e7b9b278a fix session leave issues 2022-08-08 16:31:00 +05:30
Nuwan 1c1626df73 fix not showing Account screen content 2022-08-03 21:13:51 +05:30
Nuwan 07a00505b8 fix listing audio profiles 2022-08-03 19:08:32 +05:30
Nuwan 09e1d27c64 fix async realted errors and cleanup 2022-08-03 18:38:42 +05:30
Nuwan 11915454cb replacing context.jamClient.isNativeClient() with context.JK.isQWebEngine 2022-08-02 18:51:05 +05:30
Nuwan 8cc3a6753b Merge remote-tracking branch 'origin/promised_based_api_interation' into promised_based_api_interation 2022-08-02 13:56:23 +05:30
Nuwan c4a37daa73 adding async/await for fixing issues related to promised based pi calls 2022-08-02 09:43:01 +05:30
Seth Call 08b2de242e Create context.JK.isQWebEngine property instead of gon.isQWebEngine 2022-08-01 21:49:33 -05:00
Seth Call e19819e035 Fix step 2 of the FTUE Gear Wizard by setting some async's where needed.
Also added in gon.isQWebEngine to act as flag indicating this is the native client.
2022-08-01 21:37:35 -05:00
Nuwan b32621fe42 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2022-07-30 08:28:34 +05:30
Nuwan 795ab8bd13 fixing errors related to gear wizard 2022-07-30 08:27:59 +05:30
Murali Gowrisankaran 2adf33e689 Removed/commented out video related frontend API calls 2022-07-28 01:12:30 +00:00
Nuwan c3bcd3b470 fixing audio gear related async errors 2022-07-27 16:22:01 +05:30
Nuwan efbe685641 Merge branch 'promised_based_api_interation' of bitbucket.org:jamkazam/jam-cloud into promised_based_api_interation 2022-07-27 11:04:04 +05:30
Nuwan 9586e0547a fixing async errors 2022-07-27 11:03:20 +05:30
Murali Gowrisankaran ffddf28a25 Fixed enum for client ID 2022-07-27 01:31:14 +00:00
Murali Gowrisankaran 57fb3ea333 Revert "Fixed enum for client ID"
This reverts commit ad61bb00cb.
2022-07-27 01:26:43 +00:00
Murali Gowrisankaran ad61bb00cb Fixed enum for client ID 2022-07-27 01:24:47 +00:00
Nuwan f732e0dad6 fixing error due to async nature of api calls 2022-07-26 23:32:36 +05:30
Murali Gowrisankaran 21ce5c237d Added method for getClientID 2022-07-12 21:27:50 +00:00
Nuwan 4b281538b2 fix errors getting after async changes
this includes errors in websocket-gateway due to blank data been
passed from web frontend to websocket routes
2022-06-16 00:02:54 +05:30
Nuwan 69cc22106d fix js error in gear_utls after changing jamClient calls to async 2022-06-02 18:00:03 +05:30
Nuwan 99d20a9869 handle the response returned from ws server for unavailable methods 2022-04-20 16:36:23 +05:30
Nuwan 43caffb85e work continues on changing jamClient calls to asynchronous 2022-04-18 19:08:02 +05:30
Nuwan cd93211406 wip promised based jamClient 2022-04-13 03:02:44 +05:30
Nuwan f05287f45a convert jamClient.* calls as promises using async/await 2022-04-07 22:31:35 +05:30
970 changed files with 18339 additions and 130941 deletions

View File

@ -1,25 +0,0 @@
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

View File

@ -1,41 +0,0 @@
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

View File

@ -1,8 +0,0 @@
name: Test Runner
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "Runner is working!"

3
.gitignore vendored
View File

@ -11,4 +11,5 @@ working.png
ruby/.rails5-gems
web/.rails5-gems
websocket-gateway/.rails5-gems
.pg_data/
.pg_data/
.ruby-version

View File

@ -1 +0,0 @@
2.4.1

View File

@ -2,7 +2,7 @@ ActiveAdmin.register JamRuby::AffiliateQuarterlyPayment, :as => 'Affiliate Quart
menu :label => 'Quarterly Reports', :parent => 'Affiliates'
config.sort_order = 'year desc, quarter desc, due_amount_in_cents desc'
config.sort_order = 'due_amount_in_cents DESC'
config.batch_actions = false
config.clear_action_items!
config.filters = true
@ -14,32 +14,18 @@ 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 "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 "Due (\u00A2)" do |oo| oo.due_amount_in_cents end
column 'JamTracks Sold' do |oo| oo.jamtracks_sold end
column 'Paid' do |oo| oo.paid end
column 'Closed' do |oo| oo.paid end

View File

@ -23,9 +23,7 @@ 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 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 'Partner' do |oo| link_to(oo.affiliate_partner.display_name, oo.affiliate_partner.admin_url, {:title => oo.affiliate_partner.display_name}) end
column 'Signups' do |oo| oo.signups end
column 'Visits' do |oo| oo.visits end
@ -33,16 +31,6 @@ 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

View File

@ -8,32 +8,27 @@ 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 'User Email' do |oo| oo.email end
column 'Email' do |oo| oo.email end
column 'Created' do |oo| oo.created_at 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
column 'Partner' do |oo| oo.affiliate_referral.display_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']
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
@users ||= rel.where(qq)
end
end
end

View File

@ -5,35 +5,19 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
config.sort_order = 'referral_user_count DESC'
config.batch_actions = false
# config.clear_action_items!
config.filters = true
config.per_page = 100
config.filters = false
config.per_page = 50
config.paginate = true
#form :partial => 'form'
#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("Active", default: true) { |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 })
@ -61,47 +45,14 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
column 'Code' do |oo|
oo.id
end
column 'Signups' do |oo|
column 'Referral Count' do |oo|
oo.referral_user_count
end
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
column 'Earnings' do |oo|
sprintf("$%.2f", oo.cumulative_earnings_in_dollars)
end
column 'Amount Owed' do |oo|
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
sprintf("$%.2f", oo.due_amount_in_cents.to_f / 100.to_f)
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
@ -120,15 +71,6 @@ 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

View File

@ -1,21 +0,0 @@
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

View File

@ -640,12 +640,7 @@ 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)
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
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]}%"])
end

View File

@ -17,14 +17,6 @@ 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
@ -55,14 +47,9 @@ 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 :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' }
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
end
f.actions
end

View File

@ -1,7 +1,5 @@
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'
@ -21,19 +19,6 @@ 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

View File

@ -36,10 +36,10 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
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
campaigns = User.where("origin_utm_source ILIKE '%meta%'").distinct.pluck(:origin_utm_campaign).compact.sort
campaign_ids = User.where("origin_utm_source ILIKE '%meta%'").distinct.pluck(:origin_id).compact.sort
ad_sets = User.where("origin_utm_source ILIKE '%meta%'").distinct.pluck(:origin_term).compact.sort
ad_names = User.where("origin_utm_source ILIKE '%meta%'").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
@ -49,7 +49,7 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
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;"
@ -60,9 +60,7 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
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: ''
@ -83,7 +81,6 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
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: ''
@ -92,8 +89,7 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
option c, value: c, selected: filter_ad_name == c
end
end
end
end
end
end
noscript { input type: :submit, value: "Filter" }
end
@ -129,12 +125,12 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
if filter_type == 'Organic'
query = query.where("users.origin_utm_source = 'organic'")
elsif filter_type == 'Advertising'
query = query.where("origin_utm_medium = 'cpc'")
query = query.where("users.origin_utm_source ILIKE '%meta%'")
if filter_campaign.present?
if filter_campaign == 'NULL'
query = query.where("users.origin_utm_campaign IS NULL")
elsif filter_campaign != 'All'
else
query = query.where("users.origin_utm_campaign = ?", filter_campaign)
end
end
@ -142,7 +138,7 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
if filter_campaign_id.present?
if filter_campaign_id == 'NULL'
query = query.where("users.origin_id IS NULL")
elsif filter_campaign_id != 'All'
else
query = query.where("users.origin_id = ?", filter_campaign_id)
end
end
@ -150,7 +146,7 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
if filter_ad_set.present?
if filter_ad_set == 'NULL'
query = query.where("users.origin_term IS NULL")
elsif filter_ad_set != 'All'
else
query = query.where("users.origin_term = ?", filter_ad_set)
end
end
@ -158,7 +154,7 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
if filter_ad_name.present?
if filter_ad_name == 'NULL'
query = query.where("users.origin_content IS NULL")
elsif filter_ad_name != 'All'
else
query = query.where("users.origin_content = ?", filter_ad_name)
end
end

View File

@ -1,73 +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
=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

View File

@ -0,0 +1,41 @@
class Spacer
def self.spacer(val, row)
percentage = ((val * 100) / row.total.to_f).round(1).to_s
('%-5.5s' % percentage).gsub(' ', '&nbsp;') + '%&nbsp;-&nbsp;' + 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

View File

@ -6,18 +6,15 @@ ActiveAdmin.register JamRuby::User, :as => 'UserSource' do
config.batch_actions = false
config.clear_action_items!
config.filters = false
config.per_page = 250
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') }
scope("Most Recent First", default: true) { |scope| scope.unscoped.order('created_at desc')}
index do
column "Email" do |user|
user.email
end
column "Signup (CST)" do |user|
user.created_at.in_time_zone("Central Time (US & Canada)")
column "Bought TestDrive" do |user|
!user.most_recent_test_drive_purchase.nil? ? "Yes" : "No"
end
column "UTM Source" do |user|
user.origin_utm_source
@ -28,23 +25,8 @@ 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

View File

@ -7,38 +7,6 @@ function intToIP(int) {
return part4 + "." + part3 + "." + part2 + "." + part1;
}
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");
});
/**
$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');
@ -106,5 +74,4 @@ function handleLatencyRecommendationForm(){
$(document).ready(function() {
handleUserLatencyForm();
handleLatencyRecommendationForm();
handleJamTrackRightsForm();
});

View File

@ -2,97 +2,55 @@ class ArsesController < ApplicationController
respond_to :json
def index
if params[:code] != Rails.application.config.data_dump_code
render :json => {error: "Unauthorized"}, :status => 401
return
end
@arses = JamRuby::Ars.all
render :json => @arses
end
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
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]
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}"
# 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
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?
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
@ars = ars
unless @ars.errors.any?
if ars_id.nil?
ars.reload
ars_id = ars.id_int
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
end
end

View File

@ -112,19 +112,6 @@ 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)

View File

@ -1 +0,0 @@
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

View File

@ -1,18 +0,0 @@
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

View File

@ -3,8 +3,4 @@ class JamRuby::JamTrack
# add a custom validation
def autocomplete_display_name
"#{original_artist} - #{name}"
end
end

View File

@ -20,10 +20,6 @@ JamAdmin::Application.routes.draw do
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
namespace :admin do
@ -44,8 +40,6 @@ 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"

View File

@ -1 +0,0 @@
# trigger build

View File

@ -1,5 +0,0 @@
<atlassian-ide-plugin>
<project-configuration id="1">
<servers id="2" />
</project-configuration>
</atlassian-ide-plugin>

View File

@ -1,4 +1,4 @@
image: node:14.21.3
image: node:14.17.1
pipelines:
branches:
@ -6,93 +6,33 @@ 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
- 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
- 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
artifacts:
- jam-ui/build/**
- step:
name: Deploy to staging - SPA
name: Deploy to staging
deployment: staging
script:
- pipe: atlassian/aws-s3-deploy:1.6.2
- pipe: atlassian/aws-s3-deploy:1.1.0
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
- 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
- npm install
- CI=false 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_BITBUCKET_BUILD_NUMBER=$BITBUCKET_BUILD_NUMBER REACT_APP_BITBUCKET_COMMIT=$BITBUCKET_COMMIT npm run build
artifacts:
- jam-ui/build/**
- step:
name: Deploy to production - SPA
name: Deploy to production
deployment: production
script:
- pipe: atlassian/aws-s3-deploy:1.6.2
- pipe: atlassian/aws-s3-deploy:1.1.0
variables:
S3_BUCKET: "jamkazam-ui/prd"
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"
LOCAL_PATH: "jam-ui/build"

3
build
View File

@ -1,6 +1,5 @@
#!/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
@ -62,7 +61,7 @@ if [ ! -z "$PACKAGE" ]; then
GEM_SERVER=http://localhost:9000/gems
# if still going, then push all debs up
if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* || "$GIT_BRANCH" == *feature* || "$GIT_BRANCH" == *hotfix* ]]; then
if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* || "$GIT_BRANCH" == *feature* || "$GIT_BRANCH" == *hotfix* || "$GIT_BRANCH" == *promised_based_api_interation* ]]; then
echo ""
echo "PUSHING WEB"

View File

@ -1,16 +1,7 @@
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_LEGACY_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
REACT_APP_BITBUCKET_COMMIT=dev

View File

@ -1,13 +1,7 @@
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_LEGACY_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_COOKIE_DOMAIN=.jamkazam.com
REACT_APP_GOOGLE_ANALYTICS_ID=G-MC9BTWXWY4
REACT_APP_RECURLY_PUBLIC_API_KEY=
REACT_APP_BRAINTREE_TOKEN=
REACT_APP_BITBUCKET_COMMIT=dev

View File

@ -1,13 +1,5 @@
HOST=beta.jamkazam.com
HOST=www.jamkazam.com
PORT=4000
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_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
REACT_APP_LEGACY_BASE_URL=https://www.jamkazam.com
REACT_APP_API_BASE_URL=https://www.jamkazam.com/api

View File

@ -1,13 +1,5 @@
HOST=beta.staging.jamkazam.com
PORT=4000
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_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
REACT_APP_LEGACY_BASE_URL=https://staging.jamkazam.com
REACT_APP_API_BASE_URL=https://staging.jamkazam.com/api

View File

@ -3,6 +3,6 @@
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"react/no-unescaped-entities": 0
"react/no-unescaped-entities": false
}
}

6
jam-ui/.gitignore vendored
View File

@ -30,8 +30,4 @@ yarn-error.log*
/test-results
/cypress/videos/
/cypress/screenshots/
/public/backing-tracks
/public/js
/cypress/screenshots/

View File

@ -1 +0,0 @@
v14.21.3

View File

@ -1 +0,0 @@
2.7.18

View File

@ -1,14 +1,5 @@
# 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)
@ -17,7 +8,7 @@ The DOMAIN and PORT running this app is defined in env.production file. This fil
HOST=beta.jamkazam.local
PORT=4000
REACT_APP_CLIENT_BASE_URL=http://www.jamkazam.local:3000
REACT_APP_LEGACY_BASE_URL=http://www.jamkazam.local:3000
REACT_APP_API_BASE_URL=http://www.jamkazam.local:3000/api
## Subdomains setup (development)
@ -38,19 +29,3 @@ 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)

View File

@ -1,6 +0,0 @@
build
output
node_modules
public
jam_track_tracks_for_jam_ui*

View File

@ -1 +0,0 @@
22

View File

@ -1,7 +0,0 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"ignore": [
"../src/components/e-commerce/*.js"
]
}

View File

@ -1,29 +0,0 @@
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
);

View File

@ -1,25 +0,0 @@
#!/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

View File

@ -1,39 +0,0 @@
#!/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"

View File

@ -1,337 +0,0 @@
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);

View File

@ -1,40 +0,0 @@
#!/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

View File

@ -1,32 +0,0 @@
{
"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"
}
}

View File

@ -1,55 +0,0 @@
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
},
};

View File

@ -1,22 +0,0 @@
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",
},
},
};

7
jam-ui/cypress.json Normal file
View File

@ -0,0 +1,7 @@
{
"baseUrl": "http://beta.jamkazam.local:4000",
"env": {
"legacyBaseUrl": "http://www.jamkazam.local:3000",
"apiBaseUrl": "http://www.jamkazam.local:3000/api"
}
}

View File

@ -1,166 +0,0 @@
/// <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');
});
})
});
});

View File

@ -1,34 +0,0 @@
/// <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.');
});
});

View File

@ -1,117 +0,0 @@
/// <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')
});
});

View File

@ -1,29 +0,0 @@
/// <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');
});
});

View File

@ -1,126 +0,0 @@
/// <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');
});
});

View File

@ -1,89 +0,0 @@
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');
});
})
})

View File

@ -1,137 +0,0 @@
/// <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');
});
});
});

View File

@ -1,90 +0,0 @@
/// <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');
});
})
})

View File

@ -1,54 +0,0 @@
///<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.')
});
});
});

View File

@ -1,607 +0,0 @@
/// <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)');
});
});
});
});

View File

@ -1,31 +0,0 @@
/// <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');
});
})

View File

@ -1,111 +0,0 @@
/// <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"');
});
});
});
});

View File

@ -1,41 +0,0 @@
/// <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');
});
})
});

View File

@ -1,195 +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('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')
});
})
});

View File

@ -1,463 +0,0 @@
/// <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');
});
});
});
});
});

View File

@ -1,92 +0,0 @@
/// <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')
})
})

View File

@ -1,21 +0,0 @@
/// <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!');
});
});
});

View File

@ -1,57 +0,0 @@
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
);
}

View File

@ -1,32 +0,0 @@
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
);
}

View File

@ -1,32 +0,0 @@
[
{
"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

View File

@ -1,562 +0,0 @@
{
"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
}
]
}

View File

@ -1,81 +1,74 @@
{
"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
[
{
"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"
},
{
"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": "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_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
}
]
}
"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
}
]

View File

@ -1,74 +0,0 @@
{
"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"
}
]
}

View File

@ -1,82 +0,0 @@
{
"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"
}
]
}

View File

@ -1,19 +0,0 @@
{
"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"
}
]
}

View File

@ -311,7 +311,7 @@
"genres": []
}
],
"offset": 10,
"page_count": 2,
"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",

View File

@ -285,7 +285,7 @@
"genres": []
}
],
"offset": 20,
"page_count": 2,
"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",

View File

@ -1,293 +0,0 @@
{
"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
}

View File

@ -50,6 +50,7 @@
{ "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" }
@ -73,16 +74,6 @@
{ "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,

View File

@ -1,82 +0,0 @@
[
{
"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"
}
]

View File

@ -30,9 +30,9 @@ function submitLogin(){
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')
cy.visit('/friends')
cy.url().should('include', '/authentication/basic/login')
cy.contains('Sign in')
})
})
@ -42,7 +42,7 @@ describe('Login page', () => {
})
it('validate login form', () => {
cy.visit('/auth/login')
cy.visit('/authentication/basic/login')
cy.reload()
cy.get('[data-testid=submit]').should('be.disabled')
cy.get('[data-testid=email]').type('invalid-email-format@example')
@ -54,7 +54,7 @@ describe('Login page', () => {
})
it('submit login form with invalid credentials', () => {
cy.visit('/auth/login')
cy.visit('/authentication/basic/login')
cy.reload()
cy.get('[data-testid=email]').type('peter@example.com')
cy.get('[data-testid=password]').type('wrong')
@ -63,7 +63,7 @@ describe('Login page', () => {
})
it('submits login form', () => {
cy.visit('/auth/login')
cy.visit('/authentication/basic/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')
@ -73,7 +73,7 @@ describe('Login page', () => {
it('redirect to requested page', () => {
cy.visit('/friends')
cy.url().should('include', '/auth/login')
cy.url().should('include', '/authentication/basic/login')
cy.reload()
submitLogin()
cy.url().should('eq', Cypress.config().baseUrl + '/friends')
@ -81,13 +81,4 @@ describe('Login page', () => {
})
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')
})
})

View File

@ -2,14 +2,16 @@
const showSidePanelContent = () => {
cy.get('[data-testid=profileSidePanel] h4').should('have.text', 'Test User1');
cy.get('[data-testid=profileSidePanel] .modal-body').first().within(() => {
cy.get('[data-testid=profileSidePanel] .modal-body p').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=profileSidePanel] .modal-body').within(() => {
cy.get('[data-testid=biography]').contains('Biography of Test User1');
//instruments
@ -23,14 +25,17 @@ const showSidePanelContent = () => {
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');
//cy.get('[data-testid=performance_samples]').contains('The Band')
//online presence
cy.get('[data-testid=online_presences]').contains('Soundcloud').should('have.attr', 'href').and('eq', 'https://www.soundcloud.com/testuser');
cy.get('[data-testid=online_presences]').contains('Reverbnation').should('have.attr', 'href').and('eq', 'https://www.reverbnation.com/testuser');
cy.get('[data-testid=online_presences]').contains('Bandcamp').should('have.attr', 'href').and('eq', 'https://testuser.bandcamp.com');
cy.get('[data-testid=online_presences]').contains('Fandalism').should('have.attr', 'href').and('eq', 'https://www.fandalism.com/testuser');
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 = () => {
@ -45,8 +50,7 @@ describe('Friends page without data', () => {
}).as("getPeople_empty");
});
//from_location checkbox is hidden for now. so we skip this test for now
it.skip('from_location is unchecked', () => {
it('from_location is unchecked', () => {
cy.visit('/friends');
//default api call with from_location parameter turned off
@ -59,12 +63,30 @@ describe('Friends page without data', () => {
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: false
});
//now it automatically turns on from_location parameter and fetches again
cy.wait('@getPeople_empty')
.then(interception => {
assert.isNotNull(interception.response.body, '3rd API call');
})
.its('request.body')
.should('deep.contain', {
from_location: true
});
cy.wait('@getPeople_empty')
.then(interception => {
assert.isNotNull(interception.response.body, '4th API call - (prefetched)');
})
.its('request.body')
.should('deep.contain', {
from_location: true
});
@ -79,7 +101,16 @@ describe('Friends page without data', () => {
//default api call with from_location parameter turned on
cy.wait('@getPeople_empty')
.then(interception => {
assert.isNotNull(interception.response.body, '3th API call');
assert.isNotNull(interception.response.body, '5th API call');
})
.its('request.body')
.should('deep.contain', {
from_location: true
});
cy.wait('@getPeople_empty')
.then(interception => {
assert.isNotNull(interception.response.body, '6th API call - (prefetched)');
})
.its('request.body')
.should('deep.contain', {
@ -96,14 +127,9 @@ describe('Friends page without data', () => {
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: []
});
cy.intercept('POST', /\S+\/filter\?page=1/, { fixture: 'people_page1' }).as('getPeople_page1');
cy.intercept('POST', /\S+\/filter\?page=2/, { fixture: 'people_page2' }).as('getPeople_page2');
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
});
describe('listing users', () => {
@ -116,16 +142,6 @@ describe('Friends page with data', () => {
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');
@ -138,10 +154,9 @@ describe('Friends page with data', () => {
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()
cy.get('[data-testid=peopleListTable]').within(() => {
cy.contains('Test User1').click();
});
showSidePanelContent();
closeSidePanel();
});
@ -182,7 +197,7 @@ describe('Friends page with data', () => {
.first()
.find('[data-testid=instrumentList]')
.within(() => {
cy.get('[data-testid=instrument]').should('have.length', 4); //show only 4 instruments plus more link
cy.get('div').should('have.length', 4); //show only 4 instruments plus more link
cy.contains('Acoustic Guitar: Expert');
cy.contains('Keyboard: Expert');
cy.contains('Ukulele: Expert');
@ -204,7 +219,12 @@ describe('Friends page with data', () => {
closeSidePanel();
});
it('paginate', () => {
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 10);
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');
});
});
describe('in mobile', () => {
@ -256,20 +276,6 @@ describe('Friends page with data', () => {
.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()
@ -278,6 +284,19 @@ describe('Friends page with data', () => {
showSidePanelContent();
closeSidePanel();
});
it('click connect button', () => {});
it('click message button', () => {});
it('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.get('[data-testid=peopleSwiper] .swiper-button-next').should('have.class', 'swiper-button-disabled');
});
});
});
@ -305,7 +324,7 @@ describe('Friends page with data', () => {
});
it('remove friend', () => {
cy.intercept('GET', /\S+\/profile/, { fixture: 'friend' });
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'friend' });
cy.intercept('DELETE', /\S+\/friends\S+/, { statusCode: 204, body: { ok: true } });
cy.visit('/friends');
@ -342,11 +361,11 @@ describe('Friends page with data', () => {
cy.contains('Send a message').should('exist');
});
it('is not disabled for non friends', () => {
it('is disabled for non friends', () => {
cy.get('[data-testid=peopleListTable] > tbody tr')
.eq(1)
.find('[data-testid=message]')
.should('not.be.disabled');
.should('be.disabled');
//cy.contains('You can message this user once you are friends').should('exist')
});
@ -371,10 +390,10 @@ describe('Friends page with data', () => {
messageFixtures.forEach(fixture => {
cy.fixture(fixture).then(json => {
cy.intercept('GET', /\S+\/text_messages\S+/, json);
cy.get('.modal-body .ScrollbarsCustom-Scroller')
cy.get('.modal-body .ScrollbarsCustom')
.trigger('mouseover')
.scrollTo('bottom');
cy.get('.modal-body .ScrollbarsCustom-Scroller')
cy.get('.modal-body .ScrollbarsCustom')
.trigger('mouseover')
.scrollTo('top');
numberOfMessages = numberOfMessages + 10;
@ -383,7 +402,7 @@ describe('Friends page with data', () => {
});
cy.get('button')
.contains('Close')
.contains('Cancel')
.should('not.be.disabled')
.click();
});
@ -435,7 +454,7 @@ describe('Friends page with data', () => {
.click();
cy.get('[data-testid=textMessageModal]').within(() => {
cy.get('button')
.contains('Close')
.contains('Cancel')
.should('not.be.disabled')
.click();
});
@ -447,31 +466,6 @@ describe('Friends page with data', () => {
});
});
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();
@ -555,7 +549,7 @@ describe('Friends page with data', () => {
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 => {
@ -606,6 +600,4 @@ describe('Friends page with data', () => {
});
});
});

View File

@ -0,0 +1,95 @@
/// <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()
});
it('shows homepage', () => {
cy.visit('/')
cy.contains('Home').should('exist')
showSubscribeToUpdates()
})
it("not allowed to protected page", () => {
cy.visit('/friends')
cy.url().should('include', '/authentication/basic/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('/')
});
it("shows user dropdown", () => {
showSubscribeToUpdates()
showProfileDropdown()
})
it('sign out', () => {
cy.get('[data-testid=navbarTopProfileDropdown]').contains('Peter Pan').click()
cy.stubUnauthenticate()
cy.get('[data-testid=navbarTopProfileDropdown]').contains('Sign Out').click()
cy.get('[data-testid=navbarTopProfileDropdown]').should('not.exist')
cy.contains("Home")
})
})
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').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('locale switch', () => {
beforeEach(() => {
cy.stubAuthenticate()
cy.visit('/')
})
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')
})
})
});

View File

@ -31,7 +31,6 @@ Cypress.Commands.add('stubAuthenticate', (attrs = {}) => {
first_name: 'Peter',
last_name: 'Pan',
name: 'Peter Pan',
email: 'peter@example.com',
photo_url: ''
}
const currentUserAtrs = {...defaultAttrs, ...attrs}
@ -48,58 +47,3 @@ Cypress.Commands.add('stubUnauthenticate', () => {
body: {}
}).as('getUnauthenticateCurrentUser')
});
Cypress.Commands.add('stubGonSubscriptionCodes', () => {
Cypress.on('window:before:load', win => {
win.gon = {
//session variables
global: {
subscription_codes: [
{
id: null,
name: 'Free',
price: 0,
cycle: 'month'
},
{
id: 'jamsubsilver',
name: 'Silver',
price: 4.99,
cycle: 'month'
},
{
id: 'jamsubgold',
name: 'Gold',
price: 9.99,
cycle: 'month'
},
{
id: 'jamsubplatinum',
name: 'Platinum',
price: 19.99,
cycle: 'month'
},
{
id: 'jamsubsilveryearly',
name: 'Silver',
price: 49.99,
cycle: 'year'
},
{
id: 'jamsubgoldyearly',
name: 'Gold',
price: 99.99,
cycle: 'year'
},
{
id: 'jamsubplatinumyearly',
name: 'Platinum',
price: 199.99,
cycle: 'year'
},
]
}
}
});
});

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
</head>
<body>
<div data-cy-root></div>
</body>
</html>

View File

@ -1,53 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
beforeEach(() => {
// Intercept the GET /app_features request and return an empty array
// to simulate the backend returning an empty set of app features
cy.intercept('GET', /\S+\/app_features/, {
statusCode: 200,
body: [],
}).as('getAppFeatures');
cy.intercept('GET', /\S+\/users\/\S+\/profile/, {
statusCode: 200,
body: {
id: 1,
name: 'Jane Doe',
email: 'jane@example.com',
},
}).as('getUserProfile');
cy.intercept('GET', /\S+\/users\/\S+\/my_notifications/, {
statusCode: 200,
body: [],
}).as('getMyNotifications');
cy.intercept('GET', /\S+\/users\/\S+\/friends/, {
statusCode: 200,
body: [],
}).as('getMyFriends');
});

View File

@ -1,5 +1,5 @@
// ***********************************************************
// This example support/component.js is processed and
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
@ -19,9 +19,5 @@ import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
import { mount } from 'cypress/react'
Cypress.Commands.add('mount', mount)
// Example use:
// cy.mount(<MyComponent />)

View File

@ -1,8 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts"]
}

View File

@ -13,7 +13,7 @@ const cleanCSS = require('gulp-clean-css');
-----------------------------------------------*/
gulp.task('scss', () =>
gulp
.src('src/assets/scss/theme.scss')
.src('src/assets/scss/*.scss')
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(
@ -49,7 +49,7 @@ gulp.task('scss:dark', () =>
gulp.task('scss:rtl', () =>
gulp
.src('src/assets/scss/theme.scss')
.src('src/assets/scss/*.scss')
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(

Some files were not shown because too many files have changed in this diff Show More