diff --git a/web/app/assets/javascripts/react-components/FindSessionOpen.js.jsx.coffee b/web/app/assets/javascripts/react-components/FindSessionOpen.js.jsx.coffee index e0f1bd47a..b25047214 100644 --- a/web/app/assets/javascripts/react-components/FindSessionOpen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/FindSessionOpen.js.jsx.coffee @@ -1,6 +1,7 @@ context = window SessionsActions = @SessionsActions +LatencyActions = @LatencyActions @FindSessionOpen = React.createClass({ @@ -11,7 +12,7 @@ SessionsActions = @SessionsActions registeredInfiniteScroll: false getInitialState: () -> - {active: false, sessions: [], searching: false, count: 0, currentPage: 0, end: false} + {active: false, sessions: [], searching: false, count: 0, currentPage: 0, end: false, participant_ids: []} sessionResults: () -> results = [] @@ -188,6 +189,15 @@ SessionsActions = @SessionsActions onSessionsChanged: (sessionsChanged) -> if sessionsChanged.type == @props.mode + + for session in sessionsChanged.sessions + for participant in session.active_music_session.participants + @state.participant_ids.push(participant.user.id) unless _.contains(@state.participant_ids, participant.user.id) + + for rsvp in session.approved_rsvps + @state.participant_ids.push(rsvp.id) unless _.contains(@state.participant_ids, rsvp.id) + + LatencyActions.resolve(_.unique(@state.participant_ids)) @setState(sessionsChanged) }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/FindSessionRow.js.jsx.coffee b/web/app/assets/javascripts/react-components/FindSessionRow.js.jsx.coffee index 312ffb2ac..93c5a2dd0 100644 --- a/web/app/assets/javascripts/react-components/FindSessionRow.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/FindSessionRow.js.jsx.coffee @@ -5,13 +5,16 @@ EVENTS = context.JK.EVENTS SessionsActions = context.SessionsActions AppStore = context.AppStore MAX_MINUTES_SHOW_START = 15 +SessionUtils = context.JK.SessionUtils @FindSessionRow = React.createClass({ + mixins: [Reflux.listenTo(@LatencyStore, "onLatencyUpdate")] + ui: null getInitialState: () -> - {rsvpToggle: false, openSlotToggle: false} + { rsvpToggle: false, openSlotToggle: false, userLatencies: [], userLatenciesFailed: [] } createInstrument: (participant) -> @@ -26,12 +29,31 @@ MAX_MINUTES_SHOW_START = 15 instruments + createLatencyBadge: (userId) -> + + latency = null + failed = false + + if @state.userLatencies.latencies + latency = @state.userLatencies.latencies.find((userLatency) -> + userLatency.user_id == userId + ) + + if @state.userLatenciesFailed + failed = @state.userLatenciesFailed.find((failedId) -> + failedId == userId + ) + + `` + createInSessionUser: (participant) -> - instruments = @createInstrument(participant) + instruments = @createInstrument(participant) - `` + latencyBadge = @createLatencyBadge(participant.user.id) + + `` createOpenSlot:(slot, isLast) -> @@ -73,7 +95,6 @@ MAX_MINUTES_SHOW_START = 15 {more_link}  ` - createRsvpUser: (user, session, isLast) -> instruments = [] @@ -99,10 +120,9 @@ MAX_MINUTES_SHOW_START = 15 moreLinkHtml = `{text}` + latencyBadge = @createLatencyBadge(user.id) - `` - - + `` inSessionUsersHtml: (session) -> inSessionUsers = [] @@ -121,7 +141,6 @@ MAX_MINUTES_SHOW_START = 15 ` return [result, inSessionUsers] - createRsvpUsers:(session) -> firstResults = [] @@ -144,8 +163,6 @@ MAX_MINUTES_SHOW_START = 15 [firstResults, lastResults] - - createOpenSlots: (session) -> firstResults = [] @@ -195,7 +212,6 @@ MAX_MINUTES_SHOW_START = 15
{joinText}
` - rsvpLink: (session) -> pendingRsvpId = null @@ -385,7 +401,6 @@ MAX_MINUTES_SHOW_START = 15 ) return false - inSessionMusicians: (in_session_musicians) -> if @props.mode == 'upcoming' return null @@ -399,6 +414,14 @@ MAX_MINUTES_SHOW_START = 15 createListenLink: () -> null + + onLatencyUpdate: (latencyResp) -> + logger.debug("latencyResp}}}}}}}}}}}}}}}}", latencyResp) + if latencyResp.users + @setState(userLatencies: latencyResp.users) + else if latencyResp.user_ids + @setState(userLatenciesFailed: latencyResp.user_ids) + render: () -> session = @props.session diff --git a/web/app/assets/javascripts/react-components/HoverUser.js.jsx.coffee b/web/app/assets/javascripts/react-components/HoverUser.js.jsx.coffee index 358e0c3ee..d8ad47a74 100644 --- a/web/app/assets/javascripts/react-components/HoverUser.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/HoverUser.js.jsx.coffee @@ -11,7 +11,7 @@ MAX_MINUTES_SHOW_START = 15 render: () -> user = this.props.user - + latencyBadge = this.props.latencyBadge userId = user.id name = user.name avatar_url = context.JK.resolveAvatarUrl(user.photo_url) @@ -30,6 +30,9 @@ MAX_MINUTES_SHOW_START = 15
{this.props.instruments}
+ +
{latencyBadge}
+ {this.props.more} ` diff --git a/web/app/assets/javascripts/react-components/LatencyBadge.js.jsx.coffee b/web/app/assets/javascripts/react-components/LatencyBadge.js.jsx.coffee new file mode 100644 index 000000000..71c0e6877 --- /dev/null +++ b/web/app/assets/javascripts/react-components/LatencyBadge.js.jsx.coffee @@ -0,0 +1,51 @@ +LATENCY = { + ME : {description: "ME", style: "latency-me", min: -1, max: -1}, + GOOD : {description: "GOOD", style: "latency-good", min: 0.0, max: 40.0}, + MEDIUM : {description: "FAIR", style: "latency-fair", min: 40.0, max: 70.0}, + HIGH : {description: "HIGH", style: "latency-poor", min: 70.0, max: 10000000}, + #UNACCEPTABLE: {description: "UNACCEPTABLE", style: "latency-unacceptable", min: 100, max: 10000000}, + UNKNOWN: {description: "UNKNOWN", style: "latency-unknown", min: -2, max: -2}, + FAILED: {description: "FAILED", style: "latency-failed", min: -3, max: -3} + } + +@LatencyBadge = React.createClass({ + + render: () -> + + if @props.latency + latency = @props.latency + full_score = latency.ars.total_latency + internet_score = parseInt(latency.internet_score); + audio_latency = parseInt(latency.audio_latency); + + latencyDescription = "" + latencyStyle = "" + + if !full_score || full_score <= LATENCY.UNKNOWN.max + latencyDescription = LATENCY.UNKNOWN.description + latencyStyle = LATENCY.UNKNOWN.style + + else if full_score <= LATENCY.GOOD.max + latencyDescription = LATENCY.GOOD.description + latencyStyle = LATENCY.GOOD.style; + + else if full_score <= LATENCY.MEDIUM.max + latencyDescription = LATENCY.MEDIUM.description + latencyStyle = LATENCY.MEDIUM.style + + else if full_score <= LATENCY.HIGH.max + latencyDescription = LATENCY.HIGH.description + latencyStyle = LATENCY.HIGH.style + + else if @props.failed + latencyDescription = LATENCY.FAILED.description + latencyStyle = LATENCY.FAILED.style + + else + latencyDescription = LATENCY.UNKNOWN.description + latencyStyle = LATENCY.UNKNOWN.style + + + `{latencyDescription}` + +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/session_utils.js b/web/app/assets/javascripts/session_utils.js index e3ed04e24..4cb9c7c37 100644 --- a/web/app/assets/javascripts/session_utils.js +++ b/web/app/assets/javascripts/session_utils.js @@ -16,8 +16,8 @@ ME : {description: "ME", style: "latency-me", min: -1, max: -1}, GOOD : {description: "GOOD", style: "latency-good", min: 0.0, max: 40.0}, MEDIUM : {description: "FAIR", style: "latency-fair", min: 40.0, max: 70.0}, - POOR : {description: "POOR", style: "latency-poor", min: 70.0, max: 100}, - UNACCEPTABLE: {description: "UNACCEPTABLE", style: "latency-unacceptable", min: 100, max: 10000000}, + POOR : {description: "HIGH", style: "latency-poor", min: 70.0, max: 100}, + UNACCEPTABLE: {description: "HIGH", style: "latency-poor", min: 100, max: 10000000}, UNKNOWN: {description: "UNKNOWN", style: "latency-unknown", min: -2, max: -2}, FAILED: {description: "FAILED", style: "latency-failed", min: -3, max: -3} }; diff --git a/web/app/assets/stylesheets/client/sessionList.scss b/web/app/assets/stylesheets/client/sessionList.scss index c5df7a844..e84784e36 100644 --- a/web/app/assets/stylesheets/client/sessionList.scss +++ b/web/app/assets/stylesheets/client/sessionList.scss @@ -1,7 +1,7 @@ @import "client/common"; -table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, #account-session-detail, table.payment-table, table.jamtable { +table.findsession-table, table.local-recordings, table.open-jam-tracks, table.open-backing-tracks, table.cart-items, #account-session-detail, table.payment-table, table.jamtable, .find-session-latency-badge { .latency-unacceptable { width: 50px; @@ -164,6 +164,11 @@ table.findsession-table, table.local-recordings, table.open-jam-tracks, table.op } } +.find-session-latency-badge{ + span{ + padding: 2px; + } +} .avatar-tiny { float:left; diff --git a/web/spec/features/find_sessions_latency_badge_spec.rb b/web/spec/features/find_sessions_latency_badge_spec.rb new file mode 100644 index 000000000..dfa105421 --- /dev/null +++ b/web/spec/features/find_sessions_latency_badge_spec.rb @@ -0,0 +1,225 @@ +require 'spec_helper' +require 'webmock/rspec' + +describe "Find session latency badge", js: true, type: :feature, capybara_feature: true do + let(:creator_user){ FactoryGirl.create(:user) } + let(:finder_user){ FactoryGirl.create(:user) } + let(:user1){ FactoryGirl.create(:user) } + let(:user2){ FactoryGirl.create(:user) } + let(:user3){ FactoryGirl.create(:user) } + let(:user4){ FactoryGirl.create(:user) } + let(:latency_data_uri) { /\S+\/user_latencies/ } + + def create_session(creator_user) + in_client(creator_user) do + fast_signin(creator_user, "/client#/createSession") + wait_until_curtain_gone + find("h1", text: "session") + find(".quick-start-open").click + end + + end + + def join_session(joiner_user) + in_client(joiner_user) do + fast_signin(joiner_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + find("#sessions-active .found-session .join-link").click + find("#session-terms-conditions") + within("#session-terms-conditions") do + find("#btn-accept-terms").click + end + find("h1", text: "session") + end + end + + before(:all) do + Capybara.default_max_wait_time = 10 + end + + describe "In public session" do + + before(:each) do + emulate_client + ActiveMusicSession.delete_all + create_session(creator_user) + end + + it "show GOOD" do + in_client(finder_user) do + creator_response_body = mock_latency_response([{ user: creator_user, ars_total_latency: 1.0, ars_internet_latency: 0.5, audio_latency: 0.5 }]) + + stub_request(:post, latency_data_uri) + .with( + :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'} + ) + .to_return( body: creator_response_body, status: 200) + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 1) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "GOOD") + #expect(page).to have_selector("div.latency-badge[data-user-id=\"#{joiner_user.id}\"]", text: "GOOD") + + # selector = "#sessions-active a[data-user-id=\"#{creator_user.id}\"][data-hoveraction=\"musician\"]" + # find(selector, text: creator_user.name).hover_intent + # find('h3', text: creator_user.name) + # find("#musician-latency-badge .latency", text: 'GOOD') + # page.execute_script("$('#{selector}').mouseleave();") + # sleep(1) + + # selector = "#sessions-active a[data-user-id=\"#{joiner_user.id}\"][data-hoveraction=\"musician\"]" + # find(selector, text: joiner_user.name).hover_intent + # find('h3', text: joiner_user.name) + # find("#musician-latency-badge .latency", text: 'GOOD') + # page.execute_script("$('#{selector}').mouseleave();") + # sleep(1) + end + end + + it "show FAIR" do + in_client(finder_user) do + creator_response_body = mock_latency_response([{ user: creator_user, ars_total_latency: 41.0, ars_internet_latency: 21.0, audio_latency: 20.0 }]) + + stub_request(:post, latency_data_uri) + .with( + :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'} + ) + .to_return( body: creator_response_body, status: 200) + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 1) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "FAIR") + #expect(page).to have_selector("div.latency-badge[data-user-id=\"#{joiner_user.id}\"]", text: "GOOD") + + end + end + + it "show HIGH if latency > 70", focus: true do + in_client(finder_user) do + creator_response_body = mock_latency_response([{ user: creator_user, ars_total_latency: 71.0, ars_internet_latency: 41.0, audio_latency: 30.0 }]) + + stub_request(:post, latency_data_uri) + .with( + :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'} + ) + .to_return( body: creator_response_body, status: 200) + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 1) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "HIGH") + #expect(page).to have_selector("div.latency-badge[data-user-id=\"#{joiner_user.id}\"]", text: "GOOD") + + end + end + + it "show HIGH if latency > 100", focus: true do + in_client(finder_user) do + creator_response_body = mock_latency_response([{ user: creator_user, ars_total_latency: 101.0, ars_internet_latency: 51.0, audio_latency: 50.0 }]) + + stub_request(:post, latency_data_uri) + .with( + :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'} + ) + .to_return( body: creator_response_body, status: 200) + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 1) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "HIGH") + #expect(page).to have_selector("div.latency-badge[data-user-id=\"#{joiner_user.id}\"]", text: "GOOD") + + end + end + + it "show UNKNOWN" do + in_client(finder_user) do + creator_response_body = mock_latency_response([{ user: creator_user, ars_total_latency: -2, ars_internet_latency: 0, audio_latency: 0 }]) + + stub_request(:post, latency_data_uri) + .with( + :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'} + ) + .to_return( body: creator_response_body, status: 200) + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 1) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "UNKNOWN") + #expect(page).to have_selector("div.latency-badge[data-user-id=\"#{joiner_user.id}\"]", text: "GOOD") + + end + end + + it "show FAILED" do + in_client(finder_user) do + creator_response_body = mock_latency_response([{ user: creator_user, ars_total_latency: -3, ars_internet_latency: 0, audio_latency: 0 }]) + + stub_request(:post, latency_data_uri) + .to_raise("some error") + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 1) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "FAILED") + #expect(page).to have_selector("div.latency-badge[data-user-id=\"#{joiner_user.id}\"]", text: "GOOD") + + end + end + + it "shows latency of joiners of the session" do + response_body = mock_latency_response([ + { user: creator_user, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD + { user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD + { user: user2, ars_total_latency: 71.0, ars_internet_latency: 35, audio_latency: 36 }, #HIGH + { user: user3, ars_total_latency: 41.0, ars_internet_latency: 15, audio_latency: 26.0 }, #FAIR + { user: user4, ars_total_latency: 101.0, ars_internet_latency: 31.75, audio_latency: 70.25 } #HIGH + ]) + + stub_request(:post, latency_data_uri) + .with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}) + .to_return( body: response_body, status: 200) + + [user1, user2, user3, user4].each do |user| + join_session(user) + end + + fast_signin(finder_user, "/client#/findSession") + wait_until_curtain_gone + find("h1", text: "find a session") + find("a", text: "Open Jams").click + expect(page).to have_selector("#sessions-active .found-session table.musicians-category tr", count: 5) + + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{creator_user.id}\"]", text: "GOOD") + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{user1.id}\"]", text: "GOOD") + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{user2.id}\"]", text: "HIGH") + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{user3.id}\"]", text: "FAIR") + expect(page).to have_selector("div.latency-badge[data-user-id=\"#{user4.id}\"]", text: "HIGH") + save_screenshot("findSessionLatency.png") + end + + end + +end \ No newline at end of file diff --git a/web/spec/features/musician_hover_spec_1.rb b/web/spec/features/musician_hover_spec_1.rb index 34bd4b7ca..ce956c5ec 100644 --- a/web/spec/features/musician_hover_spec_1.rb +++ b/web/spec/features/musician_hover_spec_1.rb @@ -44,7 +44,7 @@ describe "Musician Hover", :js => true, :type => :feature, :capybara_feature => end - it "show POOR" do + it "show HIGH" do response_body = mock_latency_response([ {user: user2, ars_total_latency: 71.0, ars_internet_latency: 35, audio_latency: 36 }]) #sessionUtils.LATENCY.POOR : {description: "POOR", style: "latency-poor", min: 70.0, max: 100}, stub_request(:post, latency_data_uri) .with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}) @@ -53,7 +53,7 @@ describe "Musician Hover", :js => true, :type => :feature, :capybara_feature => site_search(user2.first_name, expand: true) find("#search-results a[user-id=\"#{user2.id}\"][hoveraction=\"musician\"]", text: user2.name).hover_intent find('h3', text: user2.name) - find("#musician-latency-badge .latency", text: 'POOR') + find("#musician-latency-badge .latency", text: 'HIGH') #sleep(2) #save_screenshot("latency-poor.png") find("#musician-latency-badge .latency-info", text: 'Internet 35ms + Audio 36ms') @@ -97,11 +97,11 @@ describe "Musician Hover", :js => true, :type => :feature, :capybara_feature => page.execute_script("$('#{selector}').mouseleave();") sleep(1) - #Show badge - POOR + #Show badge - HIGH selector = "#search-results a[user-id=\"#{user3.id}\"][hoveraction=\"musician\"]" find(selector, text: user3.name).hover_intent find('h3', text: user3.name) - find("#musician-latency-badge .latency", text: 'POOR') + find("#musician-latency-badge .latency", text: 'HIGH') find("#musician-latency-badge .latency-info", text: 'Internet 35ms + Audio 36ms') page.execute_script("$('#{selector}').mouseleave();") sleep(1) @@ -115,11 +115,11 @@ describe "Musician Hover", :js => true, :type => :feature, :capybara_feature => page.execute_script("$('#{selector}').mouseleave();") sleep(1) - #Show badge - UNACCEPTABLE + #Show badge - HIGH selector = "#search-results a[user-id=\"#{user5.id}\"][hoveraction=\"musician\"]" find(selector, text: user5.name).hover_intent find('h3', text: user5.name) - find("#musician-latency-badge .latency", text: 'UNACCEPTABLE') + find("#musician-latency-badge .latency", text: 'HIGH') find("#musician-latency-badge .latency-info", text: 'Internet 31ms + Audio 70ms') page.execute_script("$('#{selector}').mouseleave();") sleep(1) diff --git a/web/spec/features/musician_hover_spec_2.rb b/web/spec/features/musician_hover_spec_2.rb index aeea8e820..48e2eaf39 100644 --- a/web/spec/features/musician_hover_spec_2.rb +++ b/web/spec/features/musician_hover_spec_2.rb @@ -56,7 +56,7 @@ describe "Musician Hover", :js => true, :type => :feature, :capybara_feature => find("#musician-latency-badge .latency-info").should have_content("") end - it "show UNACCEPTABLE" do + it "show HIGH" do response_body = mock_latency_response([{ user: user2, ars_total_latency: 101.0, ars_internet_latency: 81, audio_latency: 20 }]) #sessionUtils.LATENCY.UNACCEPTABLE : {description: "UNACCEPTABLE", style: "latency-unacceptable", min: 100.0, max: 10000000}, stub_request(:post, latency_data_uri) .with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}) @@ -65,7 +65,7 @@ describe "Musician Hover", :js => true, :type => :feature, :capybara_feature => site_search(user2.first_name, expand: true) find("#search-results a[user-id=\"#{user2.id}\"][hoveraction=\"musician\"]", text: user2.name).hover_intent find('h3', text: user2.name) - find("#musician-latency-badge .latency", text: 'UNACCEPTABLE') + find("#musician-latency-badge .latency", text: 'HIGH') #sleep(2) #save_screenshot("latency-unaccptable.png") find("#musician-latency-badge .latency-info", text: 'Internet 81ms + Audio 20ms') diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index 47a2a757a..b48177a59 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -523,10 +523,9 @@ end def emulate_client #page.driver.headers = { 'User-Agent' => ' JamKazam ' } - #page.driver.header 'User-Agent', 'JamKazam' - #page.driver.options[:headers].merge!({ 'User-Agent' => ' JamKazam ' }) - #Capybara.current_session.driver.header('User-Agent', 'JamKazam') - # page.driver.browser.header('User-Agent', ' JamKazam ') + #Rails.application.config.allow_force_native_client = true + #create_cookie(:act_as_native_client, "true") + allow_any_instance_of(ClientHelper).to receive(:is_native_client?).and_return(true) end def create_join_session(creator, joiners=[], options={})